注解编程
01 什么是注解编程
:::info
指的是在类或者方法上面加上特定的注解,完成特定功能的开发
使用注解开发方便,代码简洁,开发速度大大提供
:::
02 注解的作用
:::success
替换XML的这种配置形式,简化配置
:::
:::success
替换接口,实现双方 的契约型
:::
基础注解
01 注解扫描
在Spring的配置文件,加入组件扫描器的标签,说明注解在项目中的位置,让Spring扫描这些包,让他们生效
1、spring的配置文件,会扫描当前包及其子包
<context:component-scan base-package="包名"></context:component-scan>
2、扫描多个包的方式
- 使用多次组件扫描器,指定不同的包
- 使用分割符
;
或者是,
分割多个包名 - 指定父包
1.1 排除方式
排除策略可以叠加使用
<context:component-scan base-package="包名">
<context:exclude-filter type="类型" expression="表达式"/>
</context:component-scan>
annotation
:排除特定的注解org.springframework.stereotype.注解名
:Service,Component等等
**aspectj**
:切入点表达式,仅支持包切入点和类切入点assignable
:排除特定的类型**regex**
:正则表达式**custom**
:自定义排除策略,框架底层开发
1.2 包含方式
<!-- use-default-filters="false" : 让Spring默认的注解扫描方式失效 -->
<context:component-scan base-package="包名" use-default-filters="false">
<!--指定扫描哪一个注解-->
<context:include-filter type="" expression="包名"/>
</context:component-scan>
02 创建对象常用的注解
2.1 @Component
- 作用:替换原来的Spring配置文件中的
<bean>
标签id属性
:在@Component
标签中提供了默认的设置方式, 首字母小写class属性
:通过反射获得class
的内容
- 属性value就是对象的名称,相当于bean标签的id,value值是唯一的;如果说,不指定value属性,id值就是单词首字母小写,是由Spring提供的
- Spring配置文件覆盖注解配置内容,注解和配置文件中,id值和class值要设置保持一致
- 创建的对象在整个spring容器中只有一个
- 当一个类不确定是什么功能的时候,使用
Component
注解
衍生注解
:::success
本质上这些衍生注解就是@Component
,作用,细节,用法都是一致的
目的: 为了更准确的表示功能
:::
@Repository
:持久层注解,但是Spring整合Mybatis的过程中,不会使用这个注解或者@Component
注解@Service
:用在业务层类上面@Controller
:控制层注解
2.2 @Scope
- 控制简单对象的创建次数
- 如果说指定为 singleton ,就只会创建一次;这个值就是默认值
- 如果说指定为 prototype,就会创建多次
- XML中
<bean id="userService" class="com.spring.proxy.UserServiceImpl" scope="singleton | prototype"></bean>
- 注解
@Service
@Scope(value = "singleton")
public class UserServiceImpl implements UserService{}
2.3 @Lazy
- 作用:用来延迟创建单实例对象
- 位置在类上面,前提是有
@Component
注解
- 位置在类上面,前提是有
- 一般来说,Spring会在创建工厂的时候,创建所有单实例对象
- 注意
- 一旦使用了@Lazy注解之后,Spring会在使用这个对象的时候,进行对象的创建
- XML
<bean id="userService" class="com.spring.proxy.UserServiceImpl" lazy-init="true"></bean>
- 注解
@Service
@Lazy
public class UserServiceImpl implements UserService{}
2.4 生命周期相关的注解
:::danger
这两个注解并不是Spring提供的,是JSR520(JavaEE规范)提供的
:::
初始化方法:@PostConstruct
@PostConstruct
public void init(){
System.out.println("User.init");
}
销毁方法:@PreDestroy
@PreDestroy
public void destroy(){
System.out.println("User.destroy");
}
注意事项
- 并不是Spring提供的注解,是JSR520提供的(JavaEE规范)
03 注入相关的注解
3.1 JDK 类型
**@Value**
:::info
用于JDK基本类型的赋值
:::
1. 设置 xxx.properties,配置相应的键的值
2. Spring的工厂读取这个配置文件 <context:property-placeholder location="配置文件的位置"/>
3. 属性赋值 @Value("${key}")
:::success
- 不能应用于静态成员变量上,如果使用,获取的值为null
- 不能注入集合类型,Spring有提供了新的配置形式。比如说YAML,YML
:::
**@PropertySource **
:::info
1、用于替换Spring配置文件中的<context:property-placeholder location="配置文件的位置"/>
标签
:::
1. 设置 xxx.properties
2. 在实体类上应用 @PropertySource("classpath:/xxx.properties")
3. 属性赋值 @Value("${key}")
3.2 用户自定义类型
**@Autowired **
- 用于引用类型的赋值,使用的是自动注入原理,支持
byName
和byType
- 默认使用的是byType自动注入,注入对象的类型,必须与目标成员变量类型相同或者是其子类(实现类)
- 如果说要使用
byName
的方式
- 在属性上面加入
@Autowired
- 在属性上面加入
@Qualifier(value="bean的id")
:表示使用指定名称的bean完成赋值
- 属性required
- 是一个boolean类型的,默认就是true,表示引用赋值失败,程序报错,并终止执行
- 如果是false,说如果引用类型赋值失败,程序正常执行,引用类型是null
- 一般情况下使用true,能够尽早的排查程序中的错误
- 可以放置在对应成员变量的set方法之上,也可以直接将这个注解放置在成员变量之上
- 当放置在成员变量之上,通过反射直接对变量进行赋值,不会调用Set方法,这种方式比较推荐
- 当放置在set方法之上,会执行set方法
**@Resource **
- 来自JDK的注解,Spring中提供了对这个注解的支持
@Resouce(name="")
基于名字进行注入, 等价于@Autowired
与@Qualifier
联合实现的效果- 先使用
byName
自动注入,如果说byName赋值失败,就会使用byType
04 对于注解开发的思考
- Spring注解配置 和 配置文件配置是互通的
- 在程序员开发的类型上,可以加入对应的注解,进行对象的创建
- 应用其他程序员开发的类型时,还是需要使用配置文件进行配置
高级注解
:::success
Spring 3.X 以上
:::
01 配置Bean
@Configuration
- 用于替换XML配置文件
@Configuration
:类上加入这个注解,就成为了配置Bean;同样是@Component
的衍生注解- 可以应用
<context:component-scan>
进行扫描,但是不推荐使用 - 原有XML中的内容都可以通过配置Bean进行解决
- 使用这个注解后,使用
AnnotationConfigApplicationContext
,创建工厂,如果先让这个工厂读取xml配置文件,就在配置类上添加@ImportResource("配置文件的位置")
注解
# 方式一: 指定配置bean的Class,可以指定多个工厂
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class,AppConfig1.class);
# 方式二: 指定配置bean所在的路径,工厂会扫描这个包,查找具有@Configuration注解的类型
ApplicationContext ac = new AnnotationConfigApplicationContext("所在的包的路径");
02 logback日志的整合
:::danger
基于注解开发的时候,不能够集成Log4j
:::
2.1 引入依赖
<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>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</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>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.5</version>
</dependency>
2.2 引入logback的配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
03 @Bean
@Bean
注解在配置bean中进行使用,等同于Xml配置文件中的 标签
3.1 对象的创建
:::success
@Bean(“id值”) -------如果说不指定这个id值,方法名就是id值
:::
对象的创建
- 简单对象:直接能够通过new方式创建的对象
- 复杂对象:不同直接通过new的方式创建的对象
简单对象
@Bean
public 创建对象的类型 原来bean中的id值(){
// 方法体:创建对象
return 创建好的对象;
}
@Configuration
public class MyApp {
@Bean
public User user(){
return new User();
}
}
复杂对象
@Bean
@Scope("singleton")//控制对象的创建次数
public Connection conn() {
Connection conn = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/chat?serverTimezone=UTC",
"root",
"123456");
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return conn;
}
3.2 类型注入
3.2.1 用户自定义类型注入
// 方式一
@Configuration
public class AppConfig {
@Bean
public UserDao userDao(){
return new UserDaoImpl();
}
@Bean
public UserService userService(UserDao userDao){
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(userDao);
return userService;
}
}
// 方式二
@Configuration
public class AppConfig {
@Bean
public User user(){
return new User();
}
@Bean
public UserMapper userMapper(){
return new UserMapperImpl();
}
@Bean
public UserService userService(){
UserServiceImpl userService = new UserServiceImpl();
userService.setUserMapper(userMapper());
userService.save();
return userService;
}
}
3.2.2 JDK类型注入
@Configuration
@PropertySource("classpath:/init.properties")
public class AppConfig {
@Value("${id}")
private String id;
@Value("${name}")
private String name;
@Bean
public User user(){
User user = new User();
user.setId(id);
user.setName(name);
return user;
}
}
3.3 控制对象的创建次数
@Bean
@Scope("singleton | prototype") 默认值为singleton
04 @ComponentScan
4.1 基础使用
- 在配置bean中进行使用,等同于在XML配置文件中的
<context:component-scan base-package="">
标签 - 目的:能够进行相关注解的扫描
4.2 排除策略的使用
- 注解方式:
- 排除特定的注解:
type = FilterType.ANNOTATION, value={}
- 排除特定的类型:
type = FilterType.ASSIGNABLE_TYPE , value={}
- 切入点表达式:
type = FilterType.ASPECTJ, pattern=""
- 正则表达式:
type = FilterType.REGEX, pattern=""
- 自定义排除策略:
type = FilterType.CUSTOM, value=""
@Configuration
@ComponentScan(basePackages = "com.study",
excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Service.class})}
)
public class AppConfig {
}
4.3 包含策略的使用
@ComponentScan(basePackages = "com.frame",
useDefaultFilters = false,
includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Service.class})
}
)
05 Spring工厂创建对象的多种配置方式
5.1 多种创建对象的方式
**方式一:@Component **
- 主要用于程序员自己开发的类型
**方式二:@Bean **
- 框架提供的类型,别的程序员提供的类型
方式三:xml配置文件
- 与遗留系统整合的时候,在纯注解的开发过程中,基本不会使用
**方式四:@Import **
- 创建单个对象,会在Spring框架底层进行使用
5.2 多种创建对象的优先级
@Component 及其衍生注解
<@Bean
<XML配置
- 优先级高的配置,会覆盖优先级低的注解;前提是id值相同
- 通过配置优先级,可以解决基于注解进行配置的耦合问题,如果说对一个类不满意,可以使用优先级高的进行覆盖
06 整合多个配置信息
6.1 为什么会有多个配置信息
:::info
拆分多个配置bean的开发,是一种模块化开发的形式,也体现了面向对象各司其职的设计思想
:::
6.2 多配置信息整合的方式
:::success
多个配置Bean
:::
- 基于
basePackages
包扫描的方式
ApplicationContext ac = new AnnotationConfigApplicationContext("包名");
@Import
在一个配置Bean中引入另一个配置Bean,相当于<import resource=""/>
// 选择AppConfig1 作为主配置文件
@Configuration
@Import(AppConfig.class)
public class AppConfig1 {
@Bean
public Student student(){
return new Student();
}
}
// 创建工厂
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig1.class);
- 在工厂创建的时候,指定多个配置Bean的class对象
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig1.class,AppConfig2.class);
:::success
跨配置进行注入:通过提供成员变量并且使用@Autowired
进行注入
:::
在应用配置Bean的过程中,不管使用哪种方式进行配置信息的汇总,其操作方式都是通过成员变量加上
@Autowired
注解完成的
@Configuration
@Import(AppConfig2.class)
public class AppConfig1 {
@Autowired
private UserMapper userMapper;
@Bean
public UserService userService(){
UserServiceImpl userService = new UserServiceImpl();
userService.setUserMapper(userMapper);
return userService;
}
}
@Configuration
public class AppConfig2 {
@Bean
public UserMapper userMapper(){
return new UserMapperImpl();
}
}
:::success
配置Bean与@Component相关注解的整合
:::
@Component
public class UserMapperImpl implements UserMapper{}
@Configuration
@Import(AppConfig2.class)
@ComponentScan("注解扫描的包")
public class AppConfig1 {}
:::success
配置Bean与Spring的XML配置文件的整合
- 遗留系统的整合
- 配置覆盖
:::
public class UserDAOImpl implements UserDAO{
}
<bean id="userDAO" class="com.baizhiedu.injection.UserDAOImpl"/>
@Configuration
@ImportResource("applicationContext.xml")
public class AppConfig4 {
@Autowired
private UserDAO userDAO;
@Bean
public UserService userService() {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO);
return userService;
}
}
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig1.class);
6.3 配置Bean的底层实现原理
Spring 在配置Bean中加入
@Configuration
注解之后,底层就会通过Cglib
的代理方式,来进行对象相关的配置处理
四维一体的开发
:::info
Spring在开发一个功能的时候,有四种形式,虽然说开发方式不同,但是最终的效果是一样的
:::
- 基于schema:
<context:property-placeholder location=""/>
- 基于特定功能注解:
@PropertySource("")
- 基于原始的Bean标签:
<bean id="" class=""></bean>
- 基于@Bean注解:
@Bean