第一章、注解基础概念
1. 什么是注解编程
指的是在类或者方法上加入特定的注解(@XXX
),完成特定功能的开发
- 特定的注解 —> 特定的功能
示例:
@Component
public class XXX{
}
2. 为什么要讲解注解编程
- 注解开发方便
- 代码简洁 开发速度大大提高
- Spring开发潮流
- Spring2.x 引入注解
- Spring3.x 完善注解
-
3. 注解的作用
3.1 简化配置
3.2 接替接口的契约性
3.2.1 接口实现契约性
过往中,如果调用者和功能的开发者是两拨人,那么他们之间的开发规范(契约性)一般就是通过接口来实现
- 调用的方法名称
- 调用的方法的入参出参
- 比如,tomcat 的 Servlet 接口,调用者(Tomcat 服务器)对于来到服务器的请求,需要调用功能来处理请求,而这个调用的功能需要符合 tomcat 的规范,因此 tomcat 就设计了 Servlet 接口,所有实现了 Servlet 接口的实现类,都能被 tomcat 调用来处理请求
-
3.2.2 接口实现契约性的缺点
实现类需要完全实现接口的所有方法,如 Servlet 接口,很多时候只需要写 service 方法,却不得不写其他的四个方法(init、destory、等等)
调用双方约定一个注解,调用者直接通过反射遍历功能提供者的各个方法,找到贴有这个注解的方法,通过反射调用
- 比如:Spring 框架中的 aop 功能
- 如果通过接口的方式来实现,则功能的提供者需要作为
MethodInterceptor
接口的实现类,实现invoke()
方法 - 如果通过注解来实现,则功能的提供者只需要在功能的实现方法上贴上
@Around
注解即可
- 如果通过接口的方式来实现,则功能的提供者需要作为
通过注解的方式,在功能调用者和功能提供者之间达成约定,进而进行功能的调用。
因为注解应用更为方便灵活,所以在现在的开发中,更推荐通过注解的形式完成
4. Spring 注解的发展历程
- Spring2.x开始支持注解编程
@Component
@Service
@Scope
… ….
- 目的:提供的这些注解只是为了在某些情况下简化XML的配置,作为XML开发的有益补充
- 依旧是需要使用 XML 的
- Spring3.x
@Configuration
@Bean
… …
- 目的:彻底替换XML,基于纯注解编程
- Spring4.x、Spring5、SpringBoot
-
5. Spring注解开发耦合问题
Spring基于注解进行配置后,配置信息就与代码耦合在一起了
当对于注解不满意时,此时可以通过Spring配置文件进行覆盖,覆盖掉注解的内容,也即解耦
Spring 配置信息的优先级 注解 <
**@Configuration**
配置Bean < xml配置文件第二章、Spring的基础注解(Spring2.x)
这个阶段的注解,仅仅是简化XML的配置,并不能完全替代XML
搭建开发环境
- 设置 Spring Context 的扫描范围
<context:component-scan base-package="com.baizhiedu"/>
作用:让Spring框架在设置包及其子包中扫描对应的注解,使其生效。
1. 对象创建相关注解
1.1 @Component
- 作用:替换原有spring配置文件中的<bean标签
注意:
@Component("u")
:显示指定工厂创建对象的id值Spring配置文件覆盖注解配置内容
这些衍生注解本质上就是
**@Component**
,使用和作用和其一模一样,等同于 bean 标签- 使用衍生注解的目的是为了更加准确的表达一个类型的作用
- 如,在 Service 层使用
@Component
是完全可以的,但更建议使用@Service
,表明这个是 Service 层的 bean
- 如,在 Service 层使用
@Repository
:用于表明这是 DAO 层的 bean ```java @Repository public class UserDAO{
}
- `@Service`:用于表明这是 Service 层的 bean
```java
@Service
public class UserService{
}
@Controller
:用于表明这是 Controller 层的 bean ```java @Controller public class RegAction{
}
<a name="rrpXr"></a>
### 1.4 bean标签属性注解
`@Scope` 注解
- 作用:控制简单对象创建次数 `<bean id="" class="" scope="singleton|prototype"/>`
- 注意:不添加 `@Scope` Spring提供默认值 **singleton**
```java
@Component
@Scope("prototype")
public class User{
}
@Lazy
注解
- 作用:延迟创建单实例对象
<bean id="" class="" lazy="false"/>
注意:一旦使用了
@Lazy
注解后,Spring会在使用这个对象时候,才进行这个对象的创建1.5 bean 生命周期方法相关注解
@PostConstruct
:初始化方法,在对象注入后使用<bean init-method=""/>
- 使用场景:MQ 配置的初始化可以使用这个
- 执行顺序
- Constructor >> Autowired >> PostConstruct
@PreDestroy
:销毁方法,在销毁对象之前使用
<bean destory-method=""/>
注意
- 上述的2个注解并不是Spring提供的,为 JSR(JavaEE规范)520 的内容,但Spring很好地支持了,所以可以在Spring框架中使用这个
- 再一次的验证,通过注解实现了接口的契约性
- 有一个 Service bean 类,其有一个自定义类型属性 userDAO
- 在 xml 中配置 service 和 dao 的 <bean 标签,让 Spring 管理
- service 的 <bean 标签下,使用 <property 标签将 dao 的bean 作为属性注入给 service 的bean
- <property 这里是借用的 service 类中属性的 setter 方法进行注入的,本质是通过反射获得 setter 方法将值写入
2.1.2 @Autowired 的基本使用
- 有一个 Service bean 类,其有一个自定义类型属性 dao
- Service 类中加上
@Service
注解,Dao 类中加上@Repository
注解,让他们分别交由 Spring 容器管理 @Service
类中的 userDAO 属性的 setter 方法贴上@Autowried
注解,完成对 dao bean 的注入
2.1.3 @Autowired
细节
- 注入的方式(类型注入与 beanId 注入)
@Autowired
默认是基于类型进行注入- 基于类型的注入:注入对象的类型,必须与目标成员变量类型相同或者是其子类(实现类)
- 例如:Service 有个 UserDao 的属性,则只有实现了 UserDao 接口的实现类,如 UserDaoImpl 才能被注入这个属性
@Autowired
和@Qualifier
配合使用,可以实现 基于beanID 进行注入- 基于名字的注入:注入对象的id值,必须与
@Qualifier
注解中设置的名字相同 - 使用场景:对于有多个实现类,且多个实现类都放到了 Spring Context 中
- 如:不同集群的ES操作类,可以通过
@Qualifier
的方式,在不同的地方指定注入不同的实现类
- 基于名字的注入:注入对象的id值,必须与
@Autowired
注解放置位置- 放置在对应成员变量的set方法上
- 直接把这个注解放置在成员变量之上,Spring通过反射直接对成员变量进行注入(赋值),而不通过 setter 方法
- 同样的,
@Qualifier
也可以放到成员变量上
- 同样的,
@Resouce
- 是 JavaEE 规范 JSR250 中类似注入功能的注解,Spring 对其进行了支持
- 基于名字进行注入:
@Resouce(name="userDAOImpl")
- 实际效果就相当于:
@Autowired()
+@Qualifier("userDAOImpl")
- 实际效果就相当于:
- 基于类型进行注入:
@Resouce
,也即不加 name - 注意:如果在应用
@Resouce
注解时,名字没有配对成功,那么他会继续按照类型进行注入。
@Inject
- 是 JavaEE 规范 JSR330 中的注解,作用
@Autowired
完全一致 基于类型进行注入,需要额外导入依赖使用,基本已经废弃<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
2.2 JDK类型注入:
@Value
2.2.1 注入步骤
设置 xxx.properties 属性配置文件,里面写上键值对
id = 10
name = suns
Spring的工厂读取这个配置文件,也即在Spring 的配置文件中,指定Spring去读取这个 properties 文件
<context:property-placeholder location=""/>
- 在需要注入的 bean 的属性上,贴上
@Value("${key}")
注解,Spring 就会将 properties 文件对应 key 的 value 注入到这个属性上
问题:依旧需要在 xml 文件中使用标签指定 properties 文件的路径
2.2.2
@Value
注解使用细节@Value
注解不能应用在静态(static)成员变量上,否则赋值(注入)失败@Value
+Properties 配置文件的方式,不能注入集合类型,需要通过Spring提供新的配置形式 YAML YML (SpringBoot)2.2.3 properties 配置文件位置
@PropertySource
:指定 xxx.properties 配置文件所在位置作用:用于替换Spring配置文件中的
<context:property-placeholder location=""/>
标签- 配合
@Value
一起使用 - 开发步骤
- 设置 xxx.properties,写键值对
- 应用
@PropertySource
- 代码
3. 注解扫描详解
3.1 使用 xml 的方式
<context:component-scan base-package="com.baizhiedu"/>
-
3.1.1 排除包扫描的方式
使用示例
<context:component-scan base-package="com.baizhiedu">
<context:exclude-filter type="" expression=""/>
</context:component-scan>
排除策略的解析
assignable:排除特定的类型 不进行扫描,例如:
com.haniel.bean.User
就是类型为 User 的类都不扫描- annotation:排除特定的注解 不进行扫描,例如:
org.springframework.stereotype.Service
就是所有贴 @Service 注解的类不扫描 - aspectj:切入点表达式,实际中使用这个最多
- 包切入点:
com.baizhiedu.bean..*
- 类切入点:
*..User
- 包切入点:
- regex:正则表达式
- custom:自定义排除策略,用于框架底层开发
应用场景
- 当写自己的框架应用到Spring时,需要考虑有些类不需要Spring帮忙创建并管理,此时就需要将这些类排除出去
排除策略可以叠加使用
<context:component-scan base-package="com.baizhiedu">
<context:exclude-filter type="assignable" expression="com.baizhiedu.bean.User"/>
<context:exclude-filter type="aspectj" expression="com.baizhiedu.injection..*"/>
</context:component-scan>
3.1.2 包含方式
如果需要排除的太多,则排除规则设置过于复杂,此时就可以反其道而行之,设置包含的规则
使用示例
- 让Spring默认的注解扫描方式失效
**use-default-filters="false"**
- 默认策略就是扫描 base-package 设置的包及其子包
- 指定扫描那些注解
<context:include-filter type="" expression=""/>
扫描策略的解析<context:component-scan base-package="com.baizhiedu" use-default-filters="false">
<context:include-filter type="" expression=""/>
</context:component-scan>
- assignable:指定特定的类型,进行扫描
- annotation:指定特定的注解,进行扫描
- aspectj:切入点表达式
- 包切入点:
com.baizhiedu.bean..*
- 类切入点:
*..User
- 包切入点:
- regex:正则表达式
- custom:自定义排除策略,一般用于框架底层开发
包含的方式支持叠加
<context:component-scan base-package="com.baizhiedu" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
4. 对于注解开发注意
4.1 配置互通
Spring注解配置和配置文件的配置是互通的,也即通过注解配置的bean,可以直接在配置文件中注入到别的bean 的属性中
注解方式配置bean
@Repository
public class UserDAOImpl{
}
配置文件中使用这个bean
因为注解方式已经让 Spring 管理了UserDAOImpl类,其beanId默认为类名 userDAOImpl,注解和配置文件两种方式互动,因此配置文件配置时,可以直接拿注解配置的bean进行注入
<bean id="userService" class="com.baizhiedu.UserServiceImpl">
<property name="userDAO" ref="userDAOImpl"/>
</bean>
4.2 Spring2.x 可以使用注解的情况
自己开发的可以使用注解
@Component
及其衍生注解、@Autowired
@Value
等等这些注解,都有一个共同特点:用于我们自己开发的类,如:User UserService UserDAO UserAction 等,自己可以加上这些注解来控制类是否交由Spring管理,如何注入等
第三方的无法使用注解,需要使用xml配置文件
- 但是对于第三方的类,如 SqlSessionFactoryBean、MapperScannerConfigure 等,这些原生并不支持Spring的注解,自然没法无中生有出现这些注解,依旧需要使用 <bean 进行配置的
后面这种情况可以使用配置Bean(@Configuration
+ @Bean
) 替代 xml 配置文件+
第三章、Spring的高级注解(Spring3.x 及以上)
1. 配置Bean
1.1 作用
}
<a name="mpBRg"></a>
### 1.3 配置Bean取代了xml配置文件的作用
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1774803/1636900267277-2863aa9a-5fed-4e71-8535-652480ae6ee9.png#clientId=u303fba85-ca5e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=438&id=uf3e70b09&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1063&originWidth=2230&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1691274&status=done&style=none&taskId=ucf42558d-f70d-4b8a-94d3-529ae443874&title=&width=918)
<a name="MucQ8"></a>
### 1.4 使用的 ApplicationContext 实现类为 `AnnotationConfigApplicationContext`
- 使用 xml 配置文件时,使用的 ApplicationContext 实现类为 ClassPathXmlApplicationContext,需要传 xml 配置文件的路径
- 现在使用配置Bean取代xml配置文件后,使用的 ApplicationContext 实现类为 AnnotationConfigApplicationContext,传配置Bean的class类
- 也可以传指定配置bean所在的路径 `ApplicationContext ctx = new AnnotationConfigApplicationContext("com.baizhiedu");`让Spring自行扫描这个包下包含 `@Configuration` 的配置Bean
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1774803/1636900453383-bb5271fd-8145-4783-b3b1-165069c64f6b.png#clientId=u303fba85-ca5e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=256&id=uea58eb84&margin=%5Bobject%20Object%5D&name=image.png&originHeight=677&originWidth=2153&originalType=binary&ratio=1&rotation=0&showTitle=false&size=543509&status=done&style=none&taskId=u45ce1e90-63fc-49b0-b04b-878f4276f44&title=&width=815)
<a name="YbKrh"></a>
### 1.5 `@Configuration` 注解的本质
- **是 **`**@Component**`** 注解的衍生注解**,**将配置 bean 交由Spring Context 管理**
- 可以应用 <context:component-scan 标签进行扫描,但没有必要,因为 `@Configuration` 就是用来取代 xml 的
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1774803/1636900994446-eebe1ec4-4f15-4731-890b-5251c1b162d8.png#clientId=u303fba85-ca5e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=347&id=MvtYa&margin=%5Bobject%20Object%5D&name=image.png&originHeight=693&originWidth=1431&originalType=binary&ratio=1&rotation=0&showTitle=false&size=491738&status=done&style=shadow&taskId=u3195a343-a298-4da4-8590-bfa434e86d5&title=&width=715.5)
<a name="lFwhn"></a>
### 1.6 配置Bean开发的细节分析
- 基于注解开发使用日志:不能集成 Log4j,使用的是集成 logback
- 引入相关jar
```xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.logback-extensions</groupId>
<artifactId>logback-ext-spring</artifactId>
<version>0.1.4</version>
</dependency>
引入logback配置文件 (logback.xml)
```xml <?xml version=”1.0” encoding=”UTF-8”?>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
<a name="jt5lQ"></a>
## 2. @Bean注解
<a name="Z7naA"></a>
### 1.1 作用
- `@Bean` 注解在**配置bean**中进行使用,**等同于XML配置文件中的<bean标签**
<a name="iUXUK"></a>
### 1.2 @Bean注解的基本使用
对象的创建
- 简单对象:直接能够通过new方式创建的对象,如:User、UserService、UserDAO
- 复杂对象:不能通过new的方式直接创建的对象,如:Connection、SqlSessionFactory
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1774803/1633759288994-7ade2cdb-af37-456c-b758-109e8b2bcf92.png#clientId=uaeaf26fd-104c-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=376&id=u48be2228&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1433&originWidth=2752&originalType=binary&ratio=1&rotation=0&showTitle=false&size=862388&status=done&style=none&taskId=u9ed87821-dfb6-4cd8-810b-c42009371df&title=&width=723)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1774803/1636902749931-0fc5054a-839b-4fe4-8bf5-3d7f28654567.png#clientId=u303fba85-ca5e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=469&id=ufb3a9188&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1062&originWidth=2081&originalType=binary&ratio=1&rotation=0&showTitle=false&size=548795&status=done&style=shadow&taskId=uea1edb96-9c20-4b9b-9187-6dbeb0c33dd&title=&width=919)<br />`@Bean` 注解创建复杂对象使用工厂
- 使用工厂的方式创建复杂对象
- **一般使用在遗留系统中**,比如旧的 Spring 项目使用 xml 配置文件,现在要改成使用配置bean的方式
- **如果是新系统的话,直接使用在**`**@Bean**`**的方法中创建复杂对象并配置好后返回即可,不用绕一圈使用工厂的方式**
1. 工厂类实现 Spring 的 FactoryBean 接口
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1774803/1636902883143-1090bd54-3919-4acb-871b-6e6e4f80efc2.png#clientId=u303fba85-ca5e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=339&id=u486e646c&margin=%5Bobject%20Object%5D&name=image.png&originHeight=771&originWidth=2150&originalType=binary&ratio=1&rotation=0&showTitle=false&size=571391&status=done&style=shadow&taskId=u47b90f92-153f-4fe7-9680-6f86d6cf1ee&title=&width=944)
2. 直接在 `@Bean` 的方法里,直接new工厂的对象,调用 `getObject()` 方法就行了
```java
@Bean
public Connection conn1() {
Connection conn = null;
try {
ConnectionFactoryBean factoryBean = new ConnectionFactoryBean();
conn = factoryBean.getObject();
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
自定义id值
@Bean("id")
控制对象创建次数
@Bean
@Scope("singleton|prototype") 默认值 singleton
1.3 @Bean注解的注入
1.3.1 用户自定义类型
xml 配置文件的方式
通用的配置Bean方式
- 先通过
@Bean
配置好 userDAO,其 beanId 为方法名 - 再在待注入属性的对象的
@Bean
中调用哦 beanId进行注入就行- 注意,要定义形参 ```java @Bean public UserDAO userDAO() { return new UserDAOImpl(); }
@Bean public UserService userService(UserDAO userDAO) { UserServiceImpl userService = new UserServiceImpl(); userService.setUserDAO(userDAO); return userService; }
配置 Bean 的简化方式
- 不用形参
- 注入时直接调用 `userDAO()` **方法**
```java
@Bean
public UserDAO userDAO() {
return new UserDAOImpl();
}
@Bean
public UserService userService() {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO());
return userService;
}
1.3.2 JDK类型注入(耦合)
@Bean
public Customer customer() {
Customer customer = new Customer();
customer.setId(1);
customer.setName("xiaohei");
return customer;
}
1.3.3 JDK类型注入(解耦)
如果直接在代码中进行 set 方法的调用,会存在耦合的问题
建议使用 properties 的方式来解耦合
@Configuration
@PropertySource("classpath:/init.properties")
public class AppConfig1 {
@Value("${id}")
private Integer id;
@Value("${name}")
private String name;
@Bean
public Customer customer() {
Customer customer = new Customer();
customer.setId(id);
customer.setName(name);
return customer;
}
}
3. @ComponentScan注解
介绍
@ComponentScan
注解在配置bean中进行使用,等同于XML配置文件中的<context:component-scan>
标签
目的
- 进行相关注解的扫描 (
@Component
@Value
…@Autowired
)3.1 基本使用
```java @Configuration @ComponentScan(basePackages = “com.baizhiedu.scan”) public class AppConfig2 {<context:component-scan base-package=""/>
}
<a name="jVDnt"></a>
### 3.2 排除、包含的使用
<a name="tIjUP"></a>
#### 3.2.1 排除
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1774803/1636985641362-132a9235-ca18-4231-852c-129e93f8fd8f.png#clientId=u3bd0718e-d226-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=197&id=udf3687d4&margin=%5Bobject%20Object%5D&name=image.png&originHeight=471&originWidth=2064&originalType=binary&ratio=1&rotation=0&showTitle=false&size=718913&status=done&style=shadow&taskId=u2615f725-86fd-4458-9d99-0ecfd6d7935&title=&width=862)
```xml
<context:component-scan base-package="com.baizhiedu">
<context:exclude-filter type="assignable" expression="com.baizhiedu.bean.User"/>
</context:component-scan>
排除使用的是excludeFilters属性,其为一个数组,可以支持多个排除策略
@ComponentScan(basePackages = "com.baizhiedu.scan",
excludeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,value={Service.class}),
@ComponentScan.Filter(type= FilterType.ASPECTJ,pattern = "*..User1")})
排除策略类型
- 如果为 ANNOTATION、ASSIGNABLE_TYPE、CUSTOM,使用的为 value,如:
value = {Service.class}
如果为 ASPECTJ、REGEX,使用的是 pattern,如:
pattern = "*..User1"
type = FilterType.ANNOTATION value
.ASSIGNABLE_TYPE value
.ASPECTJ pattern
.REGEX pattern
.CUSTOM value
3.2.2 包含
<context:component-scan base-package="com.baizhiedu" use-default-filters="false">
<context:include-filter type="" expression=""/>
</context:component-scan>
包含使用的是includeFilters属性,其为一个数组,可以支持多个包含策略
注意,一定要有
useDefaultFilters = false
,让Spring放弃原有的默认包含策略@ComponentScan(basePackages = "com.baizhiedu.scan",
useDefaultFilters = false,
includeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,value={Service.class})})
包含策略类型
type = FilterType.ANNOTATION value
.ASSIGNABLE_TYPE value
.ASPECTJ pattern
.REGEX pattern
.CUSTOM value
4. Spring工厂创建对象的多种配置方式
4.1 多种配置方式的应用场景
自己开发的程序:
@Component
@Autowried
的方式
调用别人的框架、包:配置Bean 的方式
4.2 配置优先级
@Component
及其衍生注解 <@Bean
< 配置文件 bean标签
优先级高的配置 覆盖优先级低配置
举例:如果对
@Component
注解导入的bean 不满意,可以使用在配置bean(@Configuration
) 中使用@Bean
进行覆盖- 如果对 配置bean(
@Configuration
)的@Bean
导入的bean不满意,可以在xml配置文件中使用 <bean 标签进行覆盖
注意:
- 配置覆盖:id值保持一致
- 使用注解是,获取 ApplicationContext 使用的是 AnnotationConfigApplicationContext,传入的是配置bean,并没有地方传xml配置文件的地方
- 因此,Spring3.x 提供了一个注解,
@ImportResource("applicationContext.xml")
,贴在配置bean上,这样配置bean就能读取xml配置文件的配置了
@Component
public class User{
}
@Bean
public User user(){
return new User();
}
<bean id="user" class="xxx.User"/>
4.3 解决基于注解进行配置的耦合问题
@Configuration
//@ImportResource("applicationContext.xml")
public class AppConfig4 {
@Bean
public UserDAO userDAO() {
return new UserDAOImpl();
}
}
@Configuration
@ImportResource("applicationContext.xml")
public class AppConfig5{
}
applicationContext.xml
<bean id="userDAO" class="com.baizhiedu.injection.UserDAOImplNew"/>
5. 整合多个配置信息
5.1 多配置信息整合基础
多个配置信息的作用
- 大型项目中,配置Bean可能长达几千行,维护性极差
- 拆分多个配置bean的开发,是一种模块化开发的形式,也体现了面向对象各司其职的设计思想
- 如:不同功能的Bean划分到相同的配置Bean中
多配置信息整合的方式
- 第一种:多个配置Bean整合在一起
- 第二种:配置Bean与@Component相关注解的整合
- 第三种:配置Bean与SpringXML配置文件的整合:作用:1. 配置信息覆盖;2.遗留系统的整合
整合多种配置需要关注的要点
- 在创建 ApplicationContext 时,不指定具体的配置Bean,而是指定一个包扫描路径 basePackages
- 类似的,整合xml配置文件时,也在创建 ApplicationContext 时,不指定具体的xml配置文件,而是模糊匹配
- 选定一个主配置Bean,然后把其他的配置Bean作为子配置Bean引入
- 使用的是
@Import
,注意,多个配置Bean需要在同一个包下- 可以创建对象
- 也可以用于多配置Bean的整合
- 在工厂创建时,指定多个配置Bean的Class对象
缺点
将 AppConfig2 中的对象注入到 AppConfig1 中 ```java @Configuration @Import(AppConfig2.class) public class AppConfig1 {
@Autowired private UserDAO userDAO;
@Bean public UserService userService() {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO);
return userService;
} }
@Configuration public class AppConfig2 {
@Bean
public UserDAO userDAO() {
return new UserDAOImpl();
}
}
<a name="puIBq"></a>
### 5.3 配置Bean与@Component相关注解的整合
整合方式
- 在配置Bean中加上 `@ComponentScan`,扫描 `@Component` 家族的注解即可
注入方式
- 使用的 `@Autowired`
创建 ApplicationContext,传入配置Bean就行
- `ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig3.class);`
```java
@Repository
public class UserDAOImpl implements UserDAO{
}
@Configuration
@ComponentScan("xxxx")
public class AppConfig3 {
@Autowired
private UserDAO userDAO;
@Bean
public UserService userService() {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO);
return userService;
}
}
5.4 配置Bean与配置文件整合
配置方式
- 使用
@ImportResource
注入
- 使用
@Autowired
创建 ApplicationContext,传入配置Bean就行
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig4.class);
```java
public class UserDAOImpl implements UserDAO{
}
<a name="LfDet"></a>
## 6. 配置Bean底层实现原理
Spring在配置Bean中加入了@Configuration注解后,底层就会通过Cglib的代理方式,来进行对象相关的配置、处理
- 对带有 @Bean 注解的方法,进行了aop,用于控制对象的创建次数:方法调用一次,就会生成一个对象,代理后,调用配置Bean里的这个方法,就不会直接创建一个新对象,而是返回已经创建的对象
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1774803/1637252256301-446ab20a-8e58-4232-9311-6213d396feaf.png#clientId=u2655fc71-f35c-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=219&id=ud731326e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=558&originWidth=2316&originalType=binary&ratio=1&rotation=0&showTitle=false&size=617982&status=done&style=shadow&taskId=ud0109bd3-45e3-4655-8020-4513336b2ee&title=&width=907)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1774803/1637252073763-bc35941f-c751-454a-b1d4-d525e837c792.png#clientId=u2655fc71-f35c-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=355&id=ue1534a2d&margin=%5Bobject%20Object%5D&name=image.png&originHeight=709&originWidth=1354&originalType=binary&ratio=1&rotation=0&showTitle=false&size=262654&status=done&style=shadow&taskId=ubec9ec53-1dfa-401d-a3bb-e44651c6587&title=&width=677)
<a name="N8rVN"></a>
## 7. Spring 四维一体的开发思想
<a name="OXE3d"></a>
### 7.1 四维一体的概念
Spring开发一个功能的4种形式,虽然开发方式不同,但是最终效果是一样的。**因为最终使用的实现功能的类是一毛一样的**
1. 基于schema,既xml中的 `<context:`、`<aop:`
1. 基于特定功能注解(推荐)
1. 基于<bean 导入实现类
1. 基于 `@Bean` 注解 (推荐)
<a name="Xd8LY"></a>
### 7.2 四维一体的开发案例
现在想将 properties 文件中的值,注入到bean中,实现方式有四种
<a name="iVfCx"></a>
#### 7.2.1 基于 schema
- 也即在 xml 中导入指定的 schema,让 xml 支持这些额外的标签(功能),来实现特定的功能
- 这里导入 <context: 的schema,支持 property-placeholder 的功能
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1774803/1637253245260-e710d58b-cc8e-4d19-ac63-1c2bb423fa98.png#clientId=u2655fc71-f35c-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=377&id=u09edd310&margin=%5Bobject%20Object%5D&name=image.png&originHeight=876&originWidth=2018&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1076060&status=done&style=shadow&taskId=ue39de973-5958-4579-a9de-67adea42d12&title=&width=869)
<a name="pFFjv"></a>
#### 7.2.2 基于特定功能注解
- 也即 Spring 提供的能实现这个功能的注解
- 这里使用 `@PropertySource`
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1774803/1637253362812-4d543b51-1496-473d-b9c6-8c7c8674fa5d.png#clientId=u2655fc71-f35c-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=264&id=u9e202d09&margin=%5Bobject%20Object%5D&name=image.png&originHeight=602&originWidth=1065&originalType=binary&ratio=1&rotation=0&showTitle=false&size=442466&status=done&style=shadow&taskId=u28f55c8e-d64a-461a-a7d0-250cd3d6a2b&title=&width=467.5)
<a name="S2pfG"></a>
#### 7.2.3 基于原始<bean
- 不管是xml导入还是注解,最后都需要Spring提供一个实现功能的类,因为xml的新schema需要类解析实现,注解也需要具体的实现类
- 这里的读取properties 文件的实现类为 PropertySourcePlaceholderConfigure,直接在 xml 中导入这个类为bean,并将properties 文件的路径作为属性注入即可,这样 Spring 有了这个Bean,就有了这个功能
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1774803/1637253537469-bf8a3cf2-ef16-4086-baeb-9d8e94e35184.png#clientId=u2655fc71-f35c-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=72&id=u05636e76&margin=%5Bobject%20Object%5D&name=image.png&originHeight=144&originWidth=1819&originalType=binary&ratio=1&rotation=0&showTitle=false&size=287981&status=done&style=shadow&taskId=ucd6c95ba-4329-492a-b648-c7beb68e59a&title=&width=909.5)
<a name="Mh6Fb"></a>
#### 7.2.4 基于 @Bean 注解
- 与第三点的原理一样,需要将 PropertySourcePlaceholderConfigure 作为Spring 的Bean
- 只是第三点使用的xml配置文件,这里使用配置Bean
- 注意,**classpath 路径使用对象 ClassPathResource**
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1774803/1637253661713-8dec6e5a-6c6b-48c5-b30c-2355eee2e116.png#clientId=u2655fc71-f35c-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=224&id=ucf43dd3d&margin=%5Bobject%20Object%5D&name=image.png&originHeight=447&originWidth=1728&originalType=binary&ratio=1&rotation=0&showTitle=false&size=371315&status=done&style=shadow&taskId=u3a6d35f6-b36c-4bf6-b8d1-d64f48dbc03&title=&width=864)
<a name="oj0d2"></a>
## 8. 纯注解版AOP编程
<a name="wxVKv"></a>
### 8.1 整体操作
1. 应用配置Bean
1. 注解扫描
完成以上两部后,就可以进行注解开发了
<a name="FRAe5"></a>
### 8.2 开发步骤
1. 原始对象
- 使用 @Component 注解,使其称为Spring bean
java
@Service
public class UserServiceImpl implements UserService{
}
2. 创建切面类 (额外功能 切入点 组装切面)
java
@Aspect
@Component
public class MyAspect {
@Around(“execution( login(..))”)
public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println(“——aspect log ———“);
Object ret = joinPoint.proceed();
return ret;
}
}
``
3. 配置Bean开启aop
- 如果是xml配置文件,则是
--》 这种就是四位一体中的基于schema 方式
- 现在使用配置Bean,则在配置Bean的类名上贴
*@EnableAspectjAutoProxy--》 这种就是四位一体中的基于注解 方式
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1774803/1637335530044-7e78e8b0-9a43-4b33-9c18-d3f75eecf587.png#clientId=ufe80eed9-ab9c-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=119&id=u8f77df40&margin=%5Bobject%20Object%5D&name=image.png&originHeight=237&originWidth=869&originalType=binary&ratio=1&rotation=0&showTitle=false&size=160566&status=done&style=shadow&taskId=ue561b981-d7ac-4936-851f-dc8c3d73bfa&title=&width=434.5)
<a name="mfwtJ"></a>
### 8.3 注解AOP细节分析
<a name="BX12H"></a>
#### 8.3.1 代理创建方式的切换
- 基于 schema 开发方式中,可以通过
- 基于注解的开发方式,在
@EnableAspectjAutoProxy注解中,指定
proxyTargetClass属性即可切换
- true:Cglib;false:JDK
- 默认为false,也就是 JDK
-
@EnableAspectjAutoProxy(proxyTargetClass = true)`
- 默认都是 JDK
#### 8.3.2 SpringBoot AOP开发进一步简化
只需要Spring5中的前两步,也即1. 原始对象
2. 创建切面类 (额外功能 切入点 组装切面)
配置Bean 的 @EnableAspectjAutoProxy(),SpringBoot已经默认弄好了 #### 8.3.3 SpringBoot AOP 默认实现不同 Spring AOP 代理默认实现 JDK SpringBOOT AOP 代理默认实现 Cglib ## 9. 纯注解版Spring+MyBatis整合 - 基础配置 (配置Bean)
```xml 1. 连接池
@Bean public DataSource dataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(“”); dataSource.setUrl(); … return dataSource; }
SqlSessionFactoryBean
classpath:com.baizhiedu.mapper/*Mapper.xml @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setTypeAliasesPackage("");
...
return sqlSessionFactoryBean;
}
MapperScannerConfigure
@MapperScan(basePackages={“com.baizhiedu.dao”}) —-> 配置bean完成 ```
- 编码
```markdown
- 实体
- 表
- DAO接口
- Mapper文件
```
1. MapperLocations编码时通配的写法
```java //设置Mapper文件的路径 sqlSessionFactoryBean.setMapperLocations(Resource..); Resource resouce = new ClassPathResouce(“UserDAOMapper.xml”)
sqlSessionFactoryBean.setMapperLocations(new ClassPathResource(“UserDAOMapper.xml”));
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource[] resources = resolver.getResources(“com.baizhi.mapper/*Mapper.xml”); sqlSessionFactoryBean.setMapperLocations(resources)
<a name="3fdba1ed"></a>
###### 2. 配置Bean数据耦合的问题
使用 properties 文件解决
mybatis.driverClassName = com.mysql.jdbc.Driver mybatis.url = jdbc:mysql://localhost:3306/suns?useSSL=false mybatis.username = root mybatis.password = 123456 mybatis.typeAliasesPackages = com.baizhiedu.mybatis mybatis.mapperLocations = com.baizhiedu.mapper/*Mapper.xml
```java
@Component
@PropertySource("classpath:mybatis.properties")
public class MybatisProperties {
@Value("${mybatis.driverClassName}")
private String driverClassName;
@Value("${mybatis.url}")
private String url;
@Value("${mybatis.username}")
private String username;
@Value("${mybatis.password}")
private String password;
@Value("${mybatis.typeAliasesPackages}")
private String typeAliasesPackages;
@Value("${mybatis.mapperLocations}")
private String mapperLocations;
}
public class MyBatisAutoConfiguration {
@Autowired
private MybatisProperties mybatisProperties;
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(mybatisProperties.getDriverClassName());
dataSource.setUrl(mybatisProperties.getUrl());
dataSource.setUsername(mybatisProperties.getUsername());
dataSource.setPassword(mybatisProperties.getPassword());
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setTypeAliasesPackage(mybatisProperties.getTypeAliasesPackages());
//sqlSessionFactoryBean.setMapperLocations(new ClassPathResource("UserDAOMapper.xml"));
try {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources(mybatisProperties.getMapperLocations());
sqlSessionFactoryBean.setMapperLocations(resources);
} catch (IOException e) {
e.printStackTrace();
}
return sqlSessionFactoryBean;
}
}
10. 纯注解版事务编程
1. 原始对象 XXXService
<bean id="userService" class="com.baizhiedu.service.UserServiceImpl">
<property name="userDAO" ref="userDAO"/>
</bean>
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserDAO userDAO;
}
2. 额外功能
<!--DataSourceTransactionManager-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager dstm = new DataSourceTransactionManager();
dstm.setDataSource(dataSource);
return dstm
}
3. 事务属性
@Transactional
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDAO userDAO;
4. 基于Schema的事务配置
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
@EnableTransactionManager ---> 配置Bean
1. ApplicationContext ctx = new AnnotationConfigApplicationContext("com.baizhiedu.mybatis");
SpringBoot 实现思想
2. 注解版MVC整合,SpringMVC中进行详细讲解
SpringMyBatis --->DAO 事务基于注解 --> Service Controller
org.springframework.web.context.ContextLoaderListener ---> XML工厂 无法提供 new AnnotationConfigApplicationContext
11. Spring框架中YML的使用
11.1 什么是YML
YML(YAML)是一种新形式的配置文件,比XML更简单,比Properties更强大。
11.2 Properties进行配置问题
1. 定义yml文件
xxx.yml xxx.yaml
2. 语法
1. 基本语法
name: suns
password: 123456
2. 对象概念
account:
id: 1
password: 123456
3. 定义集合
service:
- 11111
- 22222
4. Spring与YML集成思路的分析
1. 准备yml配置文件
init.yml
name: suns
password: 123456
2. 读取yml 转换成 Properties
YamlPropertiesFactoryBean.setResources( yml配置文件的路径 ) new ClassPathResource();
YamlPropertiesFactoryBean.getObject() ---> Properties
3. 应用PropertySourcesPlaceholderConfigurer
PropertySourcesPlaceholderConfigurer.setProperties();
4. 类中 @Value注解 注入
5. Spring与YML集成编码
环境搭建
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.23</version>
</dependency>
最低版本 1.18
编码
```java
- 准备yml配置文件
配置Bean中操作 完成YAML读取 与 PropertySourcePlaceholderConfigure的创建 @Bean public PropertySourcesPlaceholderConfigurer configurer() {
YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
yamlPropertiesFactoryBean.setResources(new ClassPathResource("init.yml"));
Properties properties = yamlPropertiesFactoryBean.getObject();
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setProperties(properties);
return configurer;
}
- 类 加入 @Value注解 ```
6. Spring与YML集成的问题
1. 集合处理的问题
SpringEL表达式解决
@Value("#{'${list}'.split(',')}")
2. 对象类型的YAML进行配置时 过于繁琐
@Value("${account.name}")
SpringBoot @ConfigurationProperties