Spring注解驱动学习笔记
2019年10月14日11:29:36,老师:雷丰阳
DI依赖注入
1、自定义Bean(@Configuration,@Bean)
新建一个Config.java文件,加上注解@Configuration,表明这个类是Spring的配置类。
新建一个Person类:
package cn.lixingyu.springdriver.entity;
/**
* @author Rlxy93
* @time 2019/10/14 11:10
*/
public class Person {
private Integer id;
private String name;
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(Integer id, String name) {
this.id = id;
this.name = name;
}
public Person() {
}
}
在文件中添加一个方法,在方法上方加上@Bean,表明这个方法将添加进IOC容器。
@Bean(value = "lxy")
public Person person(){
return new Person(1,"Rlxy93");
}
在测试方法中测试:
@Test
public void test1(){
ApplicationContext Bean = new AnnotationConfigApplicationContext(Config.class);
Person person = (Person) Bean.getBean("lxy");
System.out.println(person);
}
最终输出:
自定义一个简单的Bean完成。
2、配置扫描器(@ComponentScan)
在Config类中加入注解:
@ComponentScan(value = {"cn.lixingyu.springdriver.controller",
"cn.lixingyu.springdriver.service","cn.lixingyu.springdriver.repository"},
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),useDefaultFilters = false)
其中:
value:扫描哪个包下的文件。
includeFilters:包含哪个类。加上Controller.class表示只扫描有Controller注解的类,但必须加上useDefaultFilters = false这个配置,否则不生效。
type:按照什么方式。
FilterType.ANNOTATION:按照注解。
FilterType.ASSIGNABLE_TYPE:按照类。
FilterType.ASPECTJ:按照ASPECTJ表达式。
FilterType.REGEX:按照正则表达式。
FilterType.CUSTOM:自定义Filter。需要实现TypeFilter。
excludeFilters:排除哪个类。
最终运行效果:
3、设置组件作用域(@Scope)
IOC容器中,组件默认是单例的(无论获取多少次,都是同一个对象)
singleton:单例。IOC容器启动时,调用Bean的方法创造对象放到IOC容器中。
prototype:多例。IOC启动时不会调用Bean的方法,在使用时才会调用,使用几次调用几次。
可以在Bean配置上方加入@Scope来设置作用域。
4、Bean懒加载(@Lazy)
懒加载只针对单例的Bean。
懒加载:容器启动时不创建对象,第一次使用时才创建对象,放入IOC容器中,由于是单例的Bean,所以无法创建多次。
5、按照条件注册Bean(@Conditional)
新建一个类,实现Condition接口,实现matches方法。
示例:判断当前操作系统,如果是windows,则把Person(1,"bill");添加进IOC容器,如果是linux,则把Person(1,"linus");添加进IOC容器。
WindowsCondition:实现Condition接口,添加方法matches。
package cn.lixingyu.springdriver.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* @author Rlxy93
* @time 2019/10/14 17:19
*/
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
System.out.println(property);
if(property.contains("Windows")){
return true;
}
return false;
}
}
LinuxCondition:同上。
package cn.lixingyu.springdriver.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* @author Rlxy93
* @time 2019/10/14 17:20
*/
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
System.out.println(property);
if(property.contains("linux")){
return true;
}
return false;
}
}
在Config中加入
@Bean
@Conditional(WindowsCondition.class)
public Person bill(){
return new Person(1,"bill");
}
@Bean
@Conditional(LinuxCondition.class)
public Person linus(){
return new Person(1,"linus");
}
运行结果:
6、给容器中快速添加组件(@Import,实现ImportSelector接口)
给容器注册组件:
1、包扫描+组件标注注解(@Controller,@Service,@Repository,@Component)。
2、@Bean,普通方式,直接返回一个实例化的对象。
4、实现ImportSelector接口,实现selectImports方法,加入要导入的类的全类名。
新建一个MyImportSelector类
package cn.lixingyu.springdriver.imports;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
/**
* @author Rlxy93
* @time 2019/10/15 10:29
*/
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
String[] classes = new String[]{"cn.lixingyu.springdriver.classes.One",
"cn.lixingyu.springdriver.classes.Two",
"cn.lixingyu.springdriver.classes.Three"};
return classes;
}
}
在Config类加上@Import(MySelectImports.class)注解即可。
运行结果:
5、实现ImportBeanDefinitionRegistrar接口。
新建一个MyImportBeanDefinitionRegisterar类
package cn.lixingyu.springdriver.imports;
import cn.lixingyu.springdriver.classes.Three;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
/**
* @author Rlxy93
* @time 2019/10/15 10:44
*/
public class MyImportBeanDefinitionRegisterar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry){
BeanDefinition one = beanDefinitionRegistry.getBeanDefinition("cn.lixingyu.springdriver.classes.One");
BeanDefinition two = beanDefinitionRegistry.getBeanDefinition("cn.lixingyu.springdriver.classes.Two");
if(one!=null && two!=null){
RootBeanDefinition three = new RootBeanDefinition(Three.class);
beanDefinitionRegistry.registerBeanDefinition("cn.lixingyu.springdriver.classes.three",three);
}
}
}
在Config类加上@Import({MyImportSelector.class,MyImportBeanDefinitionRegisterar.class})注解即可。
上述代码的意思是:如果在IOC容器中,存在One和Two的Bean,才会把Three添加进IOC容器中,否则不添加。
运行效果:
6、实现FactoryBean(这是一个工厂Bean)。
1)isSingleton方法中,返回true表示单例,返回false表示多例。
2)默认获取的是工厂Bean调用getObject()方法创建的对象。
3)在Bean前加上&可以获取工厂Bean本身。
新建一个MyFactoryBean类
package cn.lixingyu.springdriver.Factory;
import cn.lixingyu.springdriver.entity.Person;
import org.springframework.beans.factory.FactoryBean;
/**
* @author Rlxy93
* @time 2019/10/15 10:53
*/
public class MyFactoryBean implements FactoryBean<Person> {
@Override
public Person getObject() throws Exception {
System.out.println("执行了getObject方法....");
return new Person(1,"Rlxy93");
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
@Override
public boolean isSingleton() {
//true为单例,false为多例
return false;
}
}
需要在Config类中加入
@Bean
public MyFactoryBean myFactoryBean(){
return new MyFactoryBean();
}
运行效果:
7、Bean的生命周期
Bean的声明周期包含创建、初始化和销毁。Bean的生命周期由IOC容器管理,由IOC容器调用相应的Init(),Destory()方法。
1、在@Bean中配置initmethod和destorymethod即可配置好相应的初始化方法和销毁方法。
1)ps:多例的Bean不会自动销毁,容器不会管理这个Bean。
在Person类中加入
public void init(){
System.out.println("init....");
}
public void destory(){
System.out.println("destory....");
}
在Config类中加入
@Bean(initMethod = "init",destroyMethod = "destory")
public Person person2(){
return new Person(1,"Rlxy93");
}
在@Bean中添加initMethod和destoryMethod,对应Person中的init和destory方法。
运行效果:
2、实现InitializingBean接口(初始化),DisposableBean接口(销毁)。
在实体类中实现InitializingBean和DisposableBean接口
package cn.lixingyu.springdriver.entity;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
/**
* @author Rlxy93
* @time 2019/10/14 11:10
*/
public class Person implements InitializingBean, DisposableBean {
private Integer id;
private String name;
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(Integer id, String name) {
this.id = id;
this.name = name;
}
public Person() {
}
@Override
public void destroy() throws Exception {
System.out.println("destory....");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("init....");
}
}
在Config中添加
@Bean
public Person person2(){
return new Person(1,"Rlxy93");
}
ps:不需要在@Bean中写上initMethod和destroyMethod对应的方法名。
运行效果:
3、使用JSR250的注解:
1)@PostConstruct,在Bean创建完成并赋值后进行初始化。
2)@PreDestory,在销毁Bean之前通知我们进行清理工作。
在实体类中加上
@PostConstruct
public void init(){
System.out.println("init....");
}
@PreDestroy
public void destory(){
System.out.println("destory....");
}
在Config类中加上
@Bean
public Person person2(){
return new Person(1,"Rlxy93");
}
运行效果:
4、实现BeanPostProcessor接口(Bean的后置处理器)
1)Before:在初始化之前执行的方法。
2)After:在初始化之后执行的方法。
新建一个MyBeanPostProcessor类
package cn.lixingyu.springdriver.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* @author Rlxy93
* @time 2019/10/15 11:34
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行了postProcessBeforeInitialization方法...."+beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行了postProcessAfterInitialization方法...."+beanName);
return bean;
}
}
ps:
1)在类上要添加@Component注解。
2)要使用@ComponentScan("cn.lixingyu.springdriver.bean")扫描添加了@Component注解的包。
运行效果:
8、赋值(@Value)
1、基本赋值。
2、SpEL表达式,#{}(常用的表达式,如1+1,2)。
3、使用外部配置文件,使用${}取配置文件中的值。
1)使用@PropertySource加载外部文件。其中Value属性写 value="classpath:xxx.properties"
在application.properties中加上
name=lxy
age=20
在实体类中加上
@Value("${age}")
private Integer id;
@Value("${name}")
private String name;
在Config类中加上
@Bean
public Person person3(){
return new Person();
}
运行效果:
9、自动装配(@Autowired,@Qualifier,@Primary)
1、@Autowired,默认优先按照类型装配,如果存在多个同类型的Bean,再按照变量名作为Bean的id去寻找。
优先按照类型匹配
按照id匹配
ps:首先获取在IOC容器中创建好的两个对象,分别打印它的地址,再获取personService里面的使用了@Autowired注解的personRepository,两个地址一样,说明是按照id匹配的。
2、@Qualifier,指定一个id,按照Bean的id去寻找。
ps:
1)需要和@Autowired配合使用。
2)这里给定id是personRepository1,则添加到personService的是personRepository1的地址。
3、@Primary(在Bean上加这个注解destroy设定首选的Bean。
ps:这里设定了personRepository1为首选的Bean,所以与下面代码的变量名无关。
@Autowired
public PersonRepository personRepository;
4、@Resource,默认是按照变量名查找Bean。
使用@Resource注解时,按照变量名查找Bean。
5、@Inject,和@Autowired的功能一样。
10、方法、构造器的自动装配
@Autowired标注在方法上时,Spring会从IOC容器中,查找方法参数里面的对象,把它取出来,完成赋值。
@Autowired标注在构造器中时,也是在容器中获取,完成赋值。(必须是有参构造器)
@Autowired标注在参数位置时,同样是在容器中获取,完成赋值。
@Bean标注的方法创建对象时,方法参数的值从容器中获取。
ps:只有一个有参构造器时,可以不用使用@Autowired,还是可以完成赋值。
11、Spring底层组件(实现Aware接口)
1、EmbeddedValueResolverAware接口
新建一个MyEmbeddedValueResolverAware类
package cn.lixingyu.springdriver.Aware;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;
/**
* @author Rlxy93
* @time 2019/10/16 09:24
*/
@Component
public class MyEmbeddedValueResolverAware implements EmbeddedValueResolverAware {
@Override
public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
String s = stringValueResolver.resolveStringValue("hello ${os.name},i'm #{90*4}");
System.out.println(s);
}
}
在Config类中加入"cn.lixingyu.springdriver.Aware"。扫描这个包。
运行效果:
2、BeanNameAware接口
新建一个MyBeanNameAware类
package cn.lixingyu.springdriver.Aware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.stereotype.Component;
/**
* @author Rlxy93
* @time 2019/10/16 09:30
*/
@Component
public class MyBeanNameAware implements BeanNameAware {
@Override
public void setBeanName(String s) {
System.out.println("当前Bean的名字"+s);
}
}
运行结果:
12、Profile环境搭建(@Profile)
Profile:Spring为我们提供的可以根据当前环境,动态地激活、切换一系列组件的功能。
比如:有开发环境、测试环境和生产环境,可以动态的切换A数据库、B数据库和C数据库。
@Profile:里面加定义的环境标识,如:test,dev,默认是default。不加的时候,在所有环境都会生效。
新建一个MyDataSource类
package cn.lixingyu.springdriver.bean;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
/**
* @author Rlxy93
* @time 2019/10/16 09:33
*/
@Configuration
@PropertySource("classpath:application.properties")
public class MyDataSource {
@Value("${jdbc.user}")
private String user;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.driver}")
private String driver;
private ComboPooledDataSource dataSource;
{
dataSource = new ComboPooledDataSource();
}
@Profile("test")
@Bean
public DataSource test() throws PropertyVetoException {
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl("jdbc:mysql:///shiro");
return dataSource;
}
@Profile("dev")
@Bean
public DataSource dev() throws PropertyVetoException {
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl("jdbc:mysql:///shop");
return dataSource;
}
@Profile("prod")
@Bean
public DataSource prod() throws PropertyVetoException {
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl("jdbc:mysql:///spring");
return dataSource;
}
}
如何切换环境?:
1、使用命令行参数,-Dspring.profiles.active=test,表示当前环境是test环境。
运行结果:
2、使用代码
//使用无参构造器创建一个IOC容器
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
//设置需要激活的环境
ac.getEnvironment().addActiveProfile("dev");
//注册配置类
ac.register(MyDataSource.class);
//启动刷新容器
ac.refresh();
String[] names = ac.getBeanDefinitionNames();
for(String name : names){
System.out.println(name);
}
运行结果:
AOP切面
AOP切面初识(@AspectJ,@EnableAspectJAutoProxy,@Before,@After,@AfterReturning,@AfterThrowing,@Around,@PointCut)
通知方法:
前置通知:@Before,在方法执行前调用。
后置通知:@After,在方法执行之后调用。
返回通知:@AfterReturning,返回方法调用后的返回值。
异常通知:@AfterThrowing,方法异常时调用。
环绕通知:@Around,手动推进目标方法运行,joinPoint.procced();执行目标方法。
1、将业务逻辑类和切面类添加进容器中。
2、告诉Spring哪个是切面类@AspectJ。
3、在Config类上加入@EnableAspectJAutoProxy。
4、如果要获取方法名和参数列表,则可以在方法参数加上JoinPoint joinPoint。其中,返回通知则加Object result,异常通知则加Exception exception。
新建一个MyAopMethod类
package cn.lixingyu.springdriver.bean;
/**
* @author Rlxy93
* @time 2019/10/17 10:13
*/
public class MyAopMethod {
public int div(int i, int j){
System.out.println(i/j);
return i/j;
}
}
新建一个MyAop类
package cn.lixingyu.springdriver.Aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @author Rlxy93
* @time 2019/10/17 10:10
*/
@Aspect
@Component
public class MyAop {
@Pointcut("execution(public int cn.lixingyu.springdriver.bean.MyAopMethod.div(int,int))")
public void pointCut() {
}
@Before("pointCut()")
public void before() {
System.out.println("Before....");
}
@AfterReturning(value = "pointCut()", returning = "result")
public void returning(JoinPoint joinPoint, int result) {
System.out.println("Returning=" + result+","+joinPoint.getArgs()[0]+joinPoint.getArgs()[1]);
}
@AfterThrowing(value = "pointCut()", throwing = "e")
public void exception(JoinPoint joinPoint, Exception e) {
System.out.println("Exception=" + e.getMessage());
}
@After("pointCut()")
public void after(JoinPoint joinpoint) {
System.out.println("After...." + joinpoint.getSignature().getName());
}
}
在Config类中加入
@Bean
public MyAopMethod myAopMethod(){
return new MyAopMethod();
}
@Bean
public MyAop myAop(){
return new MyAop();
}
ps:joinPoint参数必须要出现在参数列表的第一位,否则会出错。
运行效果:
声明式事务
开启基于注解的事务(@EnableTransactionManagement)
1、在Config类中配置事务管理器,返回一个事务管理器,必须把dataSource注册在事务管理器中。
2、在Config类加上@EnableTransactionManagement。
3、在业务逻辑方法上加@Transaction。