Spring-注解式开发
一、注解基础概念
什么是注解编程
指的是在类或者⽅法上加⼊特定的注解(@XXX),完成特定功能的开发。
为什么要使用注解编程
- 注解开发⽅便
代码简洁 开发速度⼤⼤提⾼ - Spring开发潮流
Spring2.x引⼊注解 Spring3.x完善注解 SpringBoot普及 推⼴注解编程
注解的作用
- 替换XML这种配置形式,简化配置
- 替换接口,实现调用双方的契约性
通过注解的形式,在功能调用者和功能提供者之间达成约定,进而进行功能的调用。因为注解应用更为方便灵活,所以在现在的开发中,更推荐通过注解的形式实现
Spring注解的发展历程
- Spring2.x开始支持注解编程 @Componment @Service @Scope
目的:提供的这些注解只是为了在某些情况下简化XML的配置,作为XML开发的有益补充 - Spring3.x @Configuration @Bean..
⽬的:彻底替换XML,基于纯注解编程 - Spring4.x SpringBoot
提倡使⽤注解常⻅开发
Spring注解开发的一个问题
Spring基于注解进行配置后,还能否解耦合呢?
在Spring框架应用注解时,如果对注解配置的内容不满意,可以通过Spring配置文件进行覆盖。
二、Spring的基础注解(Spring2.x)
对象创建相关注解
-
搭建开发环境
-
@Component
作用:替换原有Spring配置文件中的\<bean标签注意:
id 属性 component注解 提供了默认的设置方式 首单词字母小写
Class 属性 通过反射获得class内容 -
@Comonent细节
- 如何显示指定工厂创建对象的id 值
@Component("u")
- 如何显示指定工厂创建对象的id 值
-
Spring配置文件覆盖注解配置内容
applicationContext.xml <bean id="u" class="com.mysite.bean"/> id 和 class 的值 要和注解中保持一致, 如果id 值和注解不一致,Spring会认为这是两个对象,创建两个bean
-
@Component的衍生注解(本质都是@Component)
@Reposity ----> XXXDAO @Service ----> XXXService @Controller----> XXXController 注意:本质上这些衍生注解就是@Component 作用<bean 细节 @Service("s") 目的:更加准确的表达一个类型的作用 注意:Spring整合MyBatis开发过程中 不使用 @Reposity
-
-
@Scope
作用:控制简单对象创建次数
注意:不提供@Scope注解,Spring提供的默认是singleton
\ -
@Lazy注解
作用:延迟创建单实例对象
注意:一旦使用了@Lazy注解后,Spring会在使用这个对象时,创建这个对象
\ -
生命周期方法相关的方法
- 初始化相关的方法 --> @PostConstruct
InitializingBean
\ - 销毁方法 --> @PreDestroy
DisposaleBean
\- 注意:
- 上述的两个注解并不是Spring 提供的,JSR(JavaEE规范)
- 再一次的验证,通过注解实现了接口的契约性
- 初始化相关的方法 --> @PostConstruct
注入相关注解
-
用户自定义类型 @Autowired
@Autowired细节- Autowired注解基于类型进行注入 [推荐]
基于类型的注入:注入对象的类型,必须与目标成员变量类型相同或者是其子类(实现类) - Autowired Qualifier 基于名字进行注入
基于名字的注入:注入对象的id 值,必须与Qualifier 注解中设置的名字相同 - Autowired 注解的放置位置
- 放置在对应成员变量的set 方法上
- 直接把这个注解放在成员变量上,Spring通过反射直接对成员变量进行注入(赋值) [推荐]
- JavaEE规范中类似功能的注解
JSR250 @Resource(name="userDaoImpl") 基于名字进行注入
@Autowired()
@Qualifier("userDaoImpl")
注意:如果在应用Resource注解时,名字没有配对成功,那么它会继续按照类型进行注入。
JSR330 @Inject 作用 @Autowired完全一致 基于类型进行注入
- Autowired注解基于类型进行注入 [推荐]
-
JDK类型 @Value
@Value注解的三步1. 设置xxx.properties id=10 key=value name=zhl key=value 2. Spring工厂读取这个配置文件 <context:property-placeholder location=""/> ==》 非注解开发,Spring也为我们提供了注解的形式 3. 代码 属性 @Value("${key}")
-
@PropertySource
作用:⽤于替换Spring配置⽂件中的\标签 开发步骤 1. 设置xxx.properties id=10 key=value name=zhl key=value 2. 应用@PropertySource 3. 代码 属性 @Value("${key}")
- @Value 注解使用细节
- @Value注解不能应用在静态成员变量上
如果应用,赋值(注入)将失败 - @Value注解 + Properties这种方式,不能注入集合类型
Spring提供新的配置形式 YAML YML
- @Value注解不能应用在静态成员变量上
- @Value 注解使用细节
只使用@Value注解或@Autowired注解 如果不能实现某一功能,可以通过Spring的配置文件配置实现
三、注解扫描详解
<context:component-scan base-package="com.mysite"/>
扫描当前包 及其 子包
排除方式
<context:component-scan base-package="com.mysite"/>
<context:exclude-filter type="" expression=""/>
type:assignable:排除特定的类型 不进行扫描
annotation:排除特定的注解 不进行扫描
aspectj :切入点表达式
包切入点:com.mysite.bean..*
类切入点:*..User
regex : 正则表达式
custom :自定义排除策略,框架底层开发
</context:component-scan>
排除策略可以叠加使用
包含方式
<context:component-scan base-package="com.mysite" use-default-filters="false">
<context:include-filter type="" expression=""/>
</context:component-scan>
1. use-default-filters="false"
作用:让Spring默认的注解扫描方式失效
2. <context:include-filter type="" expression=""/>
作用:指定扫描哪些注解
type:assignable:排除特定的类型 不进行扫描
annotation:排除特定的注解 不进行扫描
aspectj :切入点表达式
包切入点:com.mysite.bean..*
类切入点:*..User
regex : 正则表达式
custom :自定义排除策略,框架底层开发
包含的方式支持叠加
<context:component-scan>
四、Spring的高级注解(Spring3.x及以上)
@Configuration
Spring3.x 提供的新的注解,用于替换XML配置文件
@Configuration
public class AppConfig{
}
@Bean注解
@Bean注解在配置bean中(@Configuration)进行使用,等同于XML配置文件中的\<bean 标签
@Bean注解的基本使用
-
对象的创建
- 简单对象
直接能够通过new 方式创建对象
User UserService UserDao - 复杂对象
直接能够通过new 方式创建对象
Connection SqlSessionFactory- @Bean注解创建复杂对象的注意事项
一般这种方式用来整合遗留系统 /** * 通过实现FactoryBean接口创建的复杂对象 也可以 结合 @Bean注解一起使用, * @return */ @Bean public Connection conn1(){ Connection conn = null; try { ConnectionFactoryBean factoryBean = new ConnectionFactoryBean(); conn = factoryBean.getObject(); } catch (Exception e) { throw new RuntimeException(e); } return conn; }
- @Bean注解创建复杂对象的注意事项
- 简单对象
-
自定义 id 值 (默认方法名是id 值)
@Bean("id值")
-
控制对象的创建次数
@Bean @Scope("singleton | prototype") //默认是 singleton
@Bean注解的注入
- 用户自定义类型的注入
@Bean public UserDao userDao(){ return new UserDaoImpl(); } //方式1: @Bean public UserService userService(UserDao userDao){ UserServiceImpl userService = new UserServiceImpl(); userService.setUserDao(userDao); return userService; } //方式2: @Bean public UserService userService(){ UserServiceImpl userService = new UserServiceImpl(); userService.setUserDao(userDao()); return userService; }
- JDK类型的注入
@Bean public User user(){ User user = new User(); user.setId(10); user.setName("zhl"); return user; }
- JDK类型的细节分析
如果直接在代码中赋值,会有耦合,和之前一样可以通过@Value注解,.properties文件赋值@Configuration @PropertySource("classpath:/init.properties") public class AppConfig1{ @Value("${id}") private Integer id; @Value("${name}") private String name; @Bean public User user(){ User user = new User(); user.setId(id); user.setName(name); return user; } }
- JDK类型的细节分析
@ComponentScan注解
@ComponentScan注解在配置bean中进行使用,等同于XML配置⽂件中的\
⽬的:进⾏相关注解的扫描 (@Component @Value ...@Autowired)
基本使用
@Configuration
@ComponentScan(basePackages = "com.mysite.scan")
public class AppConfig2 {
}
<context:component-scan base-package="com.mysite.scan"/>
排除、包含的使用
- 排除
<context:component-scan base-package="com.baizhiedu"> <context:exclude-filter type="assignable" expression="com.baizhiedu.bean.User"/> </context:component-scan> @Configuration @ComponentScan(basePackages = "com.mysite.scan", excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Service.class}), @ComponentScan.Filter(type = FilterType.ASPECTJ,pattern = "*..User1") } ) type= FilterType.ANNOTATION value .ASSIGNABLE_TYPE value .ASPECTJ pattern .REGEX pattern .CUSTOM value
- 包含
<context:component-scan base-package="com.baizhiedu" use-default-filters="false"> <context:include-filter type="" expression=""/> </context:component-scan> @ComponentScan(basePackages = "com.mysite.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
Spring工厂创建对象的多种配置方式
多种配置方式的应用场景
配置的优先级
@Component及其衍生注解 < @Bean < 配置文件bean标签
优先级高的配置 覆盖优先级低配置
配置覆盖:id 值 保持一致
想要通过配置文件bean标签 ,进行配置,需要在 配置bean(@Configuration)上
添加@ImportResource("applicationContext.xml"),让AppConfig能够扫描的到Spring的配置文件
- 解决基于注解进行配置的耦合问题
@Configuration @ImportResource("applicationContext.xml") public class AppConfig4 { @Bean public UserDao userDao(){ return new UserDaoImpl(); } } applicationContext.xml <bean id="userDao" class="com.mysite.injection.UserDaoImplNew"/>
整合多个配置信息
- 为什么会有多个配置信息
拆分多个配置bean的开发,是⼀种模块化开发的形式,也体现了⾯向对象各司其职的设计思想 - 多配置信息整合的⽅式
- 多个配置Bean的整合
- 配置Bean与@Component相关注解的整合
- 配置Bean与SpringXML配置文件的整合
- 整合多种配置需要关注哪些要点
- 如何使多配置的信息 汇总成一个整体
- 如何实现跨配置的注入
多个配置Bean的整合
-
多配置的信息汇总
- base-package进行多个配置Bean的整合
- base-package进行多个配置Bean的整合
-
@Import
- 可以创建对象
- 多配置bean的整合
-
跨配置进行注入
在应用配置Bean的过程中,不管使用哪种方式 进⾏配置信息的汇总,其操作⽅式都是通过成员变量加⼊@Autowired注解完成。 @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(); } }
配置Bean与@Component相关注解的整合
通过@ComponentScan注解进行扫描含@Component的注解即可
@Component(或@Repository) public class UserDAOImpl implements UserDAO{ } @Configuration @ComponentScan("") public class AppConfig3 { @Autowired private UserDAO userDAO; @Bean public UserService userService() { UserServiceImpl userService = new UserServiceImpl(); userService.setUserDAO(userDAO); return userService; } } ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig3.class);
配置Bean与配置文件整合
1.遗留系统的整合 2.配置覆盖
在配置Bean中 通过@ImportResource("applicationContext.xml")进行整合
配置Bean的底层原理
Spring在配置Bean中加入了@Configuration注解后,底层就会通过Cglib的代理⽅式,来进行对象相关的配置、处理
纯注解版AOP编程
搭建环境
- 应用配置Bean
- 注解扫描
开发步骤
- 原始对象
@Service(@Component) public class UserServiceImpl implements UserService{ }
- 创建切面类(额外功能 切入点 组装切面)
@Aspect @Component //不需要在applicationContext.xml中配置bean了 <bean public class MyAspect{ @Around("execution(* login(..))") public Object arround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("------aspect log-----"); Object ret = joinPoint.proceed(); return ret; } }
- Spring的配置文件中
\被替换
@EnableAspectjAutoProxy
注解AOP细节分析
- 代理创建方式的切换 JDK Cglib
<aop:aspectj-autoproxy proxy-target-class=true|false>
默认以JDK的方式创建代理,
通过@EnableAspectjAutoProxy(proxyTargetClass=false),以Cglib创建代理 - SpringBoot AOP的开发方式
@EnableAspectjAutoProxy
已经设置好了
只需要完成开发步骤的前两步即可
Spring AOP 代理默认实现 JDK
SpringBoot AOP 代理默认实现 Cglib
纯注解版Spring+MyBatis整合
- 基础配置(配置Bean)
1. 连接池 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> @PropertySource("classpath:jdbc.properties") @Value(${jdbc.driver}) private String driverClassName; @Bean public DruidDataSource dataSource(){ DataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driverClassName); } 2. SqlSessionFactoryBean <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--为实体类的包下起别名--> <property name="typeAliasesPackage" value="com.mysite.entity"/> <property name="mapperLocations"> <list> <!--指定resource/类路径 下对应的 xxxMapper映射文件的位置和格式--> <value>classpath:com.mysite.mapper/ *Mapper.xml</value> </list> </property> </bean> @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setTypeAliasesPackage(new ClassPathResource("UserDaoMapper.xml")); ... return sqlSessionFactoryBean; } 3. MapperScannerConfigure <!--创建DAO对象 MapperScannerConfigure--> <bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/> <property name="basePackage" value="com.mysite.dao"/> </bean> @MapperScan(basePackages = {""}) -------> 配置Bean完成
- 编码
- 实体
- 表
- DAO接口
- Mapper映射文件
MapperLocations编码时通配的写法
//设置Mapper文件的路径
sqlSessionFactoryBean.setMapperLocations(Resource...);
Resource resource = new ClassPathResource("UserDaoMapper.xml");
<property name="mapperLocations">
<list>
<!--指定resource/类路径 下对应的 xxxMapper映射文件的位置和格式-->
<value>classpath:com.mysite.mapper/ *Mapper.xml</value>
</list>
</property>
如何在配置Bean中 配置多个Mapper文件呢?
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("com.mysite.mapper/*Mapper.xml");
sqlSessionFactoryBean.setMapperLocations(resources);
配置Bean数据耦合的问题
mybatis.driverClassName=com.mysql.cj.jdbc.Driver
mybatis.url=jdbc:mysql://localhost:3306/mybatis
mybatis.username=root
mybatis.password=15737979065
mybatis.typeAliasesPackages=com.mysite.mybatis
mybatis.mapperLocations=com.mysite.mapper/*Mapper.xml
@PropertySource("classpath:mybatis.properties")
public class MyBatisProperties {
@Value("${mybatis.driverClassName}")
private String driverClassName;
@Value("${mybatis.username}")
private String username;
@Value("${mybatis.url}")
private String url;
@Value("${mybatis.password}")
private String password;
@Value("${mybatis.typeAliasesPackages}")
private String typeAliasesPackages;
@Value("${mybatis.mapperLocations}")
private String mapperLocations;
//get set方法
}
@Configuration
@ComponentScan(basePackages = "com.mysite.mybatis")
@MapperScan(basePackages = "com.mysite.mybatis")
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());
try {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources(myBatisProperties.getMapperLocations());
sqlSessionFactoryBean.setMapperLocations(resources);
} catch (IOException e) {
throw new RuntimeException(e);
}
return sqlSessionFactoryBean;
}
}
纯注解版事务编程
- 原始对象 XXXService
<bean id="userService" class="com.mysite.service.UserServiceImpl"/> <property name="userDao" ref="userDao"/> </bean> @Service public UserServiceImpl implements UserService{ @Autowired private UserDao userDao; }
- 额外功能
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> @Bean public DataSourceTransactionManager dataSourceTransactionalManager(DataSource dataSource){ DataSourceTransactionManager dstm= new DataSourceTransactionManager(); dstm.setDataSource(dataSource); return dstm; }
- 事务属性
@Transactional @Service public class UserServiceImpl implements UserService{ @Autowired private UserDao userDao; }
- 基于Schema的事务配置
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/> @EnableTransactionalManagement ------> 配置Bean