IOC注解开发

spring IOC 注解开发 注解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。

注解开发入门

创建Maven工程,添加依赖

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>

使用@Component注解配置管理的资源

  • eg: 需要给AccountServiceImpl
@Component("accountService")//如果没有指定id,则会使用类名首字母改小写作为对象的id
public class AccountServiceImpl implements AccountService {} // 等同于 在xml里面,<bean id="accountService" class="具体的类">

引入context的命名空间

  • applicationContext.xml中需要引入context的命名空间,可在xsd-configuration.html中找到
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
</beans>

配置扫描

在beans标签内部,使用context:component-scan ,让spring扫描该基础包下的所有子包注解

<context:component-scan base-package="com.jwang"></context:component-scan>

注解开发进阶

用于创建对象的

相对于相当于: <bean id="" class="">

@Component

作用:
​ 把资源让 spring 来管理。相当于在 xml 中配置一个 bean。
属性:
​ value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。

@Controller @Service @Repository

​ web里面的三层结构中的所有类,在spring里面都称之为 Component (组件) , 但是它也提供了更详细的注解来针对不同的层级声明 。

​ 三个衍生注解如下:

          @Controller               :修饰WEB层类 —>web | SpringMVC

        @Service                    :修饰业务层类 —>service

          @Repository             :修饰DAO层类 —>dao

​ 在需要spring创建对象的类上面使用注解 @Component(“us”) 即可.Spring看类上是否有该注解,如果有该注解,生成这个类的实例。如果不指定value值, 那么默认的id值就是类名的名字, 第一个字母小写.

用于改变作用范围的@Scope

@Scope

​ singleton: 单例(默认)

​ prototype:多例

​ @Scope注解用来描述类的作用范围的,默认值singleton。如同xml中bean标签的属性scope <bean scope=""/>.如果配置成多例的使用prototype。

@Scope("prototype")
@Component("accountService")
public class AccountServiceImpl implements AccountService {}

和生命周期相关的

  • 初始化和销毁回调方法对应的注解

    ​ @PostConstrut:如同xml中bean标签的属性init-method <bean init-method=""/>,用来设置spring框架初始化此类实例时调用的初始化方法,标注在此类的初始化方法上

    ​ @PreDestroy:如同xml中bean标签的属性destroy-method <bean destroy-method=""/>,用来设置spring框架销毁此类实例时调用的销毁方法,标注在此类的销毁方法上

    注意:这两个注解都是配在方法上的

使用注解注入属性

@Value

  • 作用:

    注入基本数据类型和 String 类型数据的

  • 属性:

    value:用于指定值 , 可以通过表达式动态获得内容再赋值

  • 实例:

@Value("奥巴马")
private String name;

@Autowired

  • 作用:

    ​ 自动按照类型注入。当使用注解注入属性时, set 方法可以省略。它只能注入其他 bean 类型。

    ​ 如果只有一个实现类, 可以自动注入成功

    ​ 如果有两个或者两个以上的实现类, 找到变量名一致的id对象给注入进去, 如果找不到,就报错

  • 实例:

@Component("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
//当有多个AccountDao实现类时候, @Autowired会在在Spring容器里面找id为accountDao的对象注入,找不到就报错
private AccountDao accountDao;
@Override
public void save() {
System.out.println("AccountServiceImpl---save()");
accountDao.save();
}
}

@Qualifier

  • 作用

        在自动按照类型注入(@Autowired)的基础之上,再按照Bean的id注入。

    ​ 它在给字段注入时不能独立使用,必须和@Autowire一起使用;

    ​ 但是给方法参数注入时,可以独立使用。

  • 属性

    ​ value:指定bean的id。

  • 实例

package com.jwang.controller;

import com.jwang.service.UserService;
import org.springframework.stereotype.Controller;

import javax.annotation.Resource;

/**
* 包名:com.jwang.controller
*
* @author Leevi
* 日期2020-08-11 09:08
* 注入对象类型的属性:
* 1. Autowired: 自动装配, 如果spring的核心容器中,只有一个该类型的对象,则自动把那个对象注入给当前属性;
* 如果spring的核心容器中,不止有一个该类型的对象,那么就会根据属性名匹配对象的id,匹配上哪个就注入哪个;
* 如果一个都匹配不上,那么我们还可以通过Qualifier指定要注入的对象的id
*/
@Controller
public class UserController {
@Autowired
@Qualifier("userServiceImplAnother")
private UserService userService;
public String getName(){
//调用业务层的getName()方法
return userService.getName();
}
}

@Resource

如果上面一个接口有多种实现,那么现在需要指定找具体的某一个实现,那么可以使用@Resource

package com.jwang.controller;

import com.jwang.service.UserService;
import org.springframework.stereotype.Controller;

import javax.annotation.Resource;

/**
* 包名:com.jwang.controller
*
* @author Leevi
* 日期2020-08-11 09:08
* 注入对象类型的属性:
* 1. Autowired: 自动装配, 如果spring的核心容器中,只有一个该类型的对象,则自动把那个对象注入给当前属性;
* 如果spring的核心容器中,不止有一个该类型的对象,那么就会根据属性名匹配对象的id,匹配上哪个就注入哪个;
* 如果一个都匹配不上,那么我们还可以通过Qualifier指定要注入的对象的id
*
* 2. Resource: 能够进行自动装配以及手动装配
*/
@Controller
public class UserController {
@Resource(name = "userServiceImpl")
private UserService userService;
public String getName(){
//调用业务层的getName()方法
return userService.getName();
}
}

混合开发

注解和XML比较

img

  • xml
    • 优点: 方便维护, 改xml文件
    • 缺点: 相对注解而言, 麻烦一点
  • 注解
    • 优点: 开发简洁方便
    • 缺点: 维护没有xml那么方便, 需要改源码

混合开发特点

​ IOC: 自己写的类使用注解进行IOC,非自己写的类使用配置文件进行IOC

​ DI: 如果一个类是使用注解进行IOC的,那么它的属性使用注解进行注入;如果一个类是使用配置文件进行IOC,那么它的属性使用配置文件进行注入

混合开发环境搭建

  • 创建spring配置文件,编写头信息配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    ">
    </beans>
  • 配置扫描

    <context:component-scan base-package="com.jwang" />
  • 需要用配置文件进行IOC和依赖注入的就写在配置文件中

  • 需要使用注解进行IOC和依赖注入的就在类中添加注解

引入外部properties文件

<context:property-placeholder location="classpath:jdbc.properties"/>

<!--1. 创建DataSource对象-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
</bean>

使用mybatis和注解改造增删改查案例

  • AccountController的代码
package com.jwang.controller;

import com.jwang.pojo.Account;
import com.jwang.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import java.sql.SQLException;
import java.util.List;

/**
* 包名:com.jwang.controller
*
* @author Leevi
* 日期2020-08-09 15:06
*/
@Controller
public class AccountController {
@Autowired
private AccountService accountService;

public List<Account> findAll() throws SQLException {
return accountService.findAll();
}

public Account findById(int id) throws SQLException {
return accountService.findById(id);
}

public void deleteById(int id) throws SQLException {
accountService.deleteById(id);
}

public void add(Account account) throws SQLException {
accountService.add(account);
}

public void update(Account account) throws SQLException {
accountService.update(account);
}
}
  • 使用注解配置业务层
package com.jwang.service.impl;

import com.jwang.dao.AccountDao;
import com.jwang.pojo.Account;
import com.jwang.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.SQLException;
import java.util.List;

/**
* 包名:com.jwang.service.impl
*
* @author Leevi
* 日期2020-08-09 15:05
*/
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;

@Override
public List<Account> findAll() throws SQLException {
return accountDao.findAll();
}

@Override
public Account findById(int id) throws SQLException {
return accountDao.findById(id);
}

@Override
public void deleteById(int id) throws SQLException {
accountDao.deleteById(id);
}

@Override
public void add(Account account) throws SQLException {
accountDao.add(account);
}

@Override
public void update(Account account) throws SQLException {
accountDao.update(account);
}
}
  • AccountDao的代码
package com.jwang.dao;

import com.jwang.pojo.Account;

import java.sql.SQLException;
import java.util.List;

/**
* 包名:com.jwang.dao
*
* @author Leevi
* 日期2020-08-09 14:56
*/
public interface AccountDao {
/**
* 查询所有账号信息
* @return
*/
public List<Account> findAll() throws SQLException;

/**
* 根据id查询账号信息
* @param id
* @return
*/
public Account findById(int id) throws SQLException;

/**
* 根据id删除账号信息
* @param id
*/
public void deleteById(int id) throws SQLException;

/**
* 添加账号信息
* @param account
*/
public void add(Account account) throws SQLException;

/**
* 修改账号信息
* @param account
*/
public void update(Account account) throws SQLException;
}
  • AccountDao.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jwang.dao.AccountDao">
<insert id="add" parameterType="Account">
insert into account(name,money) values (#{name},#{money})
</insert>
<update id="update" parameterType="Account">
update account set name=#{name},money=#{money} where id=#{id}
</update>
<delete id="deleteById" parameterType="int">
delete from account where id=#{id}
</delete>

<select id="findAll" resultType="Account">
select * from account
</select>

<select id="findById" resultType="Account" parameterType="int">
select * from account where id=#{id}
</select>
</mapper>
  • 修改applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--混合开发-->
<!--1. 包扫描-->
<context:component-scan base-package="com.jwang"/>
<!--
整合mybatis:
1. 配置DataSource
2. 配置SqlSessionFactoryBean
注入数据源
注入别名包扫描
3. 配置MapperScannerConfigurer,进行Dao接口的包扫描
注入要扫描的dao接口所在的包
-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="username" value="root"></property>
<property name="password" value="123"></property>
<property name="url" value="jdbc:mysql:///day20?characterEncoding=utf8"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="typeAliasesPackage" value="com.jwang.pojo"></property>
</bean>
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.jwang.dao"></property>
</bean>
</beans>
  • log4j.properties
log4j.rootLogger=DEBUG,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#[%-5p] %t %l %d %rms:%m%n
#%d{yyyy-MM-dd HH:mm:ss,SSS\} %-5p [%t] {%c}-%m%n
log4j.appender.stdout.layout.ConversionPattern=[%-5p] %t %l %d %rms:%m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=D:\\idea_project\\jwang_mm_backend.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS\} %-5p [%t] {%c}-%m%n

使用纯注解改造增删改查

​ 基于注解的IoC配置已经完成,但是大家都发现了一个问题:我们依然离不开spring的xml配置文件,那么能不能不写这个applicationContext.xml,所有配置都用注解来实现呢?

纯注解开发原因:

  1. 方便大家学习SpringBoot
  2. 以防万一真遇到了Spring纯注解开发(不可能)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--包扫描,扫描注解-->
<context:component-scan base-package="com.jwang"/>
<!--spring整合mybatis的配置文件-->
<!--
要进行ssm的整合,第一步:将sqlSessionFactory对象交给spring核心容器管理
sqlSessionFactoryBean对象,就是管理SQLSession创建Dao代理对象的
创建代理对象的目的是执行SQL语句

-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--将数据源注入进来-->
<property name="dataSource" ref="dataSource"/>
<!--注入核心配置文件的路径-->
<property name="configLocation" value="classpath:SqlMapConfig.xml"/>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="root"/>
<property name="password" value="123"/>
<property name="url" value="jdbc:mysql:///day11?characterEncoding=utf-8"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<!--
第二步:一定要让spring核心容器去扫描dao接口,去加载dao接口的代理对象
-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定要扫描的dao接口的包-->
<property name="basePackage" value="com.jwang.dao"/>
</bean>
</beans>

实现

@Configuration

  • 作用:

    用于指定当前类是一个spring配置类,当创建容器时会从该类上加载注解。

    获取容器时需要使用AnnotationApplicationContext(类.class)。

  • 属性:

    value:用于指定配置类的字节码

  • 示例代码:

@Configuration
public class SpringConfig {

}

@ComponentScan

  • 作用:

    用于指定spring在初始化容器时要扫描的包。作用和在spring的xml配置文件中的: <context:component-scan base-package="com.jwang"/>是一样的。

  • 属性:

    basePackages:用于指定要扫描的包。和该注解中的value属性作用一样。

  • 示例代码:

@Configuration
@ComponentScan(basePackages = "com.jwang")
public class SpringConfig {

}

@Bean

  • 作用:

    该注解只能写在方法上,表明使用此方法创建一个对象,并且放入spring容器。

  • 属性:

    name:给当前@Bean注解方法创建的对象指定一个名称(即bean的id)。

  • 示例代码:

@Configuration
@ComponentScan(basePackages = "com.jwang")
public class SpringConfig {
@Bean
public DataSource getDataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUsername("root");
dataSource.setPassword("123");
dataSource.setUrl("jdbc:mysql:///day20?characterEncoding=utf8");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
return dataSource;
}

@Bean
public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
//设置dataSource属性
sqlSessionFactoryBean.setDataSource(dataSource);
//设置typeAliasesPackage
sqlSessionFactoryBean.setTypeAliasesPackage("com.jwang.pojo");
return sqlSessionFactoryBean;
}

@Bean
public MapperScannerConfigurer getMapperScannerConfigurer(){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.jwang.dao");
return mapperScannerConfigurer;
}
}

通过注解获取容器

ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);