Spring中的注解_学习笔记

1. @Configuration 配置注解

用xml的配置方式时导入组件至容器时需要在xml添加标签,并进行赋值等,利用注解的方式后只需新建一个配置类,在类上添加@Configuration,类中写相应的方法,返回需要的类型即可,需要在方法上添加@bean注解; 这种方式的组件名为默认为方法名;

2. @ComponentScan 扫描注解

spring默认扫描只会扫描启动类所在包及子包,需要指定包扫描路径,以前用xml配置需要添加<context:component-scan base-package="com.xxx"/>,采用注解开发后,只需在启动类上添加@ComponentScan注解,并指定路径即可;省略了配置文件添加包扫描。

@ComponentScan("com.atguigu")
public class MainFunction {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainFunction.class);
		String[] names = context.getBeanDefinitionNames();
		for(String name:names) {
			System.out.println(name);
		}
	}
}

亲测: 其实添加包扫描注解可以在任何一个组件上指定,且可以多次指定;

//@ComponentScan("com.atguigu")
public class MainFunction {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(UserDao.class);
		String[] names = context.getBeanDefinitionNames();
		for(String name:names) {
			System.out.println(name);
		}
	}
}
//
@Repository
//@ComponentScan("com.atguigu")
@ComponentScan("com.atguigu.config")
public class UserDao {
}

上面把启动类上的包扫描注掉,启动时加载UserDao组件,而在UserDao组件中再添加扫描路径,可得到不同的输出结果;
注: 扫描的前提,该组件必须标注了spring容器能够识别的标识(注解);

2.1 @ComponentScan注解中的属性

value:指定要扫描件的包
excludeFilters: 指定扫描的 时候按照什么规则排除那些组件;
includeFilters : 指定扫描的时候只需要包含哪些组件; 这个生效必须指定useDefaultFilters = false
此三个属性都可以放数组;

@ComponentScan(value = "com.atguigu",includeFilters = {@Filter(type =FilterType.ANNOTATION,classes = {Controller.class} )}
                                            ,excludeFilters = {@Filter(type = FilterType.ANNOTATION,classes = {Service.class})} ,useDefaultFilters = false)
public class MainFunction {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainFunction.class);
		String[] names = context.getBeanDefinitionNames();
		for(String name:names) {
			System.out.println(name);
		}
	}
}

2.2 扫描过滤的类型

@Filter注解中,type的类型可以是:
FilterType.ANNOTATION : 按照注解
FIlterType.ASSIGNABLE_TYPE : 按照给定的类型;
FilterType.ASPECTJ : 使用ASPECTJ表达式
FilterType.REGEX : 使用正则指定
FilterType.CUSTOM : 使用自定义规则

2.2.1 自定义过滤类型

仅需实现TypeFilter接口即可,例如:⬇

public class MyTypeFilter implements TypeFilter{

	/**
	 * metadataReader :读取到的当前正在扫描的类的信息
	 * metadataReaderFactory : 可以获取到其他任何类信息的
	 */
	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException {
		// TODO Auto-generated method stub
//		//获取当前类注解的信息
//		AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//		//获取当前正在扫描的类的类信息
//		ClassMetadata classMetadata = metadataReader.getClassMetadata();
//		//获取当前类 类资源(路径)
//		Resource resource = metadataReader.getResource();
		System.out.println("------->"+resource);
//		String className = classMetadata.getClassName();
//		System.out.println(">>>>>>>>>>>>>>>>>"+className);
		//逻辑....
		return false;
	}
}
@ComponentScan(value = "com.atguigu",includeFilters = @Filter(type = FilterType.CUSTOM,classes =MyTypeFilter.class),useDefaultFilters = false)
public class MainFunction {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainFunction.class);
		String[] names = context.getBeanDefinitionNames();
		for(String name:names) {
			System.out.println(name);
		}
	}
}

注: 使用自定义扫描时,指定的路径下会扫描所有类,包括没有添加三层注解的类。

3. @Scope

使用这个注解可以给Ioc容器中的bean设置作用域。xml的配置方式可以在标签的属性中设置;注解方式可以直接用@Scope来完成;

组件的作用域有四种:
singleton: 单例(默认值) , ioc容器启动会调用方法创建对象;以后每次用会从容器拿(map.get())
prototype: 多实例: ioc启动时不会去调用方法创建,每次需要获取时才会调用方法创建对象;
request : 同一个请求创建一个实例;
session: 同一个session 创建一个实例;

3.1 bean的懒加载

标注@Lazy注解的bean在容器初始化时不会被创建
懒加载只针对单例模式下。默认情况下单例模式在容器启动的时候就会初始化bean,如果想单例模式下第一次获取bean时再创建,可以给该bean设为懒加载,使用@Lazy;

4. @Conditional 条件注解

按照一定的条件进行判断,满足条件给容器中注册bean
在这里插入图片描述
从源码得知,@Conditional注解的值是class类型(该类型集成了Condition)数组,
编写条件判断

public class DogCondition02 implements Condition {

	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		// TODO Auto-generated method stub
		return true;
	} 
}

使用条件注解

	@Bean
	@Conditional(DogCondition02.class)
	public Dog dog02() {
		System.out.println("小黄222222出生了.......");
		return new Dog("小黄", 3);
	}

5. 组件导入

给容器中注册组件的方式

  1. 包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)-----有局限性,不能导入第三方的组件
  2. @Bean 导入的第三方包里面的组件
  3. @Import 快速给容器中导入一个组件
    1. @Import (要导入到容器中的组件) : 容器中就会自动注册这个组件,id默认是全类名;
    2. ImportSelector选择器: 返回需要导入的数组的全类名数组;
    3. ImportBeanDefinitionRegistrar:手动注册
  4. 使用Spring提供的FactoryBean(工厂bean)
    1. 默认获取到的是工厂bean调用getObject创建的对象
    2. 要获取工厂bean本身,需要给id前面加&
      在这里插入图片描述

5.1 使用选择器导入组件

step1:编写自定义组件选择器,需要实现ImportSelector接口

//自定义组件选择器,返回需要导入的组件数组
public class MyImportSelector implements ImportSelector{
	/**
	 * 返回值: 导入到容器中组件全类名
	 * AnnotationMetadata: 当前标注@Import注解的类的所有注解信息
	 */
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		// TODO Auto-generated method stub
		return new String[] {"com.atguigu.bean.Blue","com.atguigu.bean.Red"};
	} 
}

step2: 用@Import注解导入组件,示例代码中yell.class组件为Import单个导入,在自定义选择其中上面倒入了Blue和Red;

@Configuration
//@ComponentScan("com.atguigu")
@Import({MyImportSelector.class,Yell.class})
public class MainConfig {
//	@Bean
//	public Person getPerson() {
//	return	new Person("zhangsan",20);
//	}
}

最终Ioc容器中包含了Blue,Red以及Yell组件;

5.2 ImportBeanDefinitionRegistrar 手动注册

step1 : 编写注册器,需实现ImportBeanDefinitionRegistrar

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * AnnotationMetadata : 当前类的注解信息
	 * BeanDefinitionRegistry : BeanDefinition注册类;把所有需要添加到容器中的bean:
	 *      调用 void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 
	 */
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		// 指定bean定义信息(bean类型,等...)
		RootBeanDefinition beanDefinition = new RootBeanDefinition(Apple.class);
		//注册一个bean,指定bean名
		registry.registerBeanDefinition("apple", beanDefinition);
	}
}

step 2: 使用@Import时导入编写好的注册器
在这里插入图片描述

5.3 使用FactoryBean 注册组件

step1 : 创建自定义的FactoryBean,实现 FactoryBean接口

//创建一个Spring定义的FactoryBean
public class ColorFactoryBean  implements FactoryBean<Color>{ //泛型为要创建对象的类型
	//返货一个Color对象,这个对象会添加到容器中
	public Color getObject() throws Exception {
		// TODO Auto-generated method stub
		return new Color();
	}

	public Class<?> getObjectType() {
		// TODO Auto-generated method stub
		return Color.class;
	}
	//创建的对象是不是单例
	public boolean isSingleton() {
		// TODO Auto-generated method stub
		return false;
	}

}

step2 : 在配置中添加bean组件

@Bean
	public  ColorFactoryBean colorFactoryBean() {
		return new ColorFactoryBean();
	}

step3 : 获取bean
1) 默认获取到的是工厂bean调用getObject创建的对象
2) 要获取工厂bean本身,需要给id前面加&

@Test
	public void test02() {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
		 Object bean = context.getBean("colorFactoryBean");
		 Object bean02 = context.getBean("&colorFactoryBean");
		 System.out.println("----------->>>"+bean.getClass());
		 System.out.println("----------->>>"+bean02.getClass());
	}

在这里插入图片描述

6.组件/bean的生命周期相关注解

bean的生命周期大致分为: bean的创建 >> 初始化 >> 销毁
执行构造(即对象的创建):
scope=singleton的实例:在容器启动的时候创建;scope=prototype的实例:在每次获取的时候创建对象;

6.1 通过指定初始化和销毁方法来管理bean的创建过程

@Bean注解中的属性initMethod和destroyMethod设置响应的方法名

  @Bean(initMethod = "init",destroyMethod = "destroy")
    public Car car(){
        return  new Car();
    }
  //*******************
  public class Car {
    public Car() {
        System.out.println("Car()..........");
    }
    public void init(){
        System.out.println("Car ------init()");
    }
    public void destroy(){
        System.out.println("car ---------destroy");
    }
}

6.2 通过bean实现InitializingBean和DisposableBean的方式来管理bean的创建过程

6.3 使用JSR250中的@PostConstructh和@PreDestroy注解来管理

在初始化方法上添加@PostConstruct,在销毁方法上添加@PreDestroy;

6.4 使用BeanPostProcessor(bean的后置处理器)来管理

初始化前后进行调用

BeanPostProcessor.postProcessBeforeInitialization
初始化: 对象创建完成,并赋值号,调用初始化方法
BeanPostProcessor.postProcessAfterInitialization
销毁: 单实例:容器关闭的时候; 多实例: 容器不会管理这个bean,容器不会调用销毁方法;

new Ioc容器 >> refresh刷新容器–初始化所有单实例对象

7. 属性赋值相关注解

7.1 使用@Value赋值

// 使用@Value赋值
//1.基本数值
//2.可以写SpEL: #{}
//3.可以写${};  取出配置文件properties中的值(在运行环境变量里面的值)
	@Value("#{100+1}")
	private Integer id;
	@Value("张三")
	private String name;
	@Value("${person.address}")
	private String address;

可以在配置类中使用@PropertySource注解指定哪个properties

@PropertySource(value = {"classpath:/person.properties"})
@Configuration
public class MyConfig01 {
}

如果不用注解指定,可以在xml配置文件中添加
在这里插入图片描述

8. 自动装配注解

8.1 @Autowired

自动装配: Spring利用依赖注入(DI),完成对IOC容器找那个各个组件的依赖关系赋值;

  1. @Autowired默认优先按照类型去容器中找对应的组件:applicationContext.getBean(Xxx.class);
  2. 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找:applicationContext.getBean(“xxx”);
  3. @Qualifier(“xxx”): 使用@Qualifier指定需要装配的组件的id,而不是使用属性名
  4. 自动装配默认一定要讲属性赋好值,没有就会报错;
    可以使用@Autowired(required=false);
  5. @Primary: 让Spring进行自动装配的时候,默认使用首选的bean;
    也可以继续使用@Qualifier指定需装配的bean的名字;

8.2 @Resource(JSR-250)注解和@Inject(JSR-330)

  1. @Resource: 可以和@Autowired一样实现自动装配注解,默认按照组件名称进行装配;
    没有支持@Primary功能; 没有required的属性;
  2. @Inject : 需要导入javax.Inject的包,和@Autowired的功能一样,没有required属性;

8.3 @Autowired注解的标注位置

@Autowired 注解可以放在构造器,参数, 方法, 属性 上, 都是从容器中获取参数组件的值;

  1. 标注在方法位置上: @Bean + 方法参数:参数从容器中获取;默认不写@Autowired效果一样;都能自动注入(赋值);
  2. 标注在构造器上: 如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取;
  3. 标注在参数位置上:

8.4 自定义组件想要使用spring底层的一些组件时

当自己定义的组件需要使用spring容器底层提供的组件(如ApplicationContext,BeanFactory,等等)时,自定义的组件需实现(组件名+Aware),如ApplicationContextAware;
在创建对象的时候,会调用接口规定的方法注入相关组件,把Spring底层组件注入到自定义的bean中;

9. @Profile

根据当前环境,动态的激活和切换一系列组件的功能
@Profile : 指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何情况下都能注册在组件中;

@Profile("test")
@Bean
public Person person(){
}

激活环境标识
1 . 使用命令行动态参数: 在虚拟机参数位置加载: -Dspring.profile.active=test
2. 使用java代码方式 : Environment
由于有参构造的方式创建的applicationcontext中没有设置环境的环节,如图:⬇
在这里插入图片描述
所以需要使用无参构造的方式创建applicationcontext,然后获取环境信息并设置值(步骤仿照上面的源码步骤:⬇)

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.getEnvironment().setActiveProfiles("test","dev");
		context.register(MyDataSourceConfig.class);
		context.refresh();

10. AOP相关

AOP : 动态代理: 指在 程序运行期间动态的将某段代码切入到指定方法、指定位置进行运行的编译方式;

10.1 使用aop需导入spring-aspects依赖

	<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.3.12.RELEASE</version>
    </dependency>

step1 : 定义一个业务逻辑类

public class MathCalculator {
    public int div(int i,int j){
        return  i/j;
    }
}

step 2 : 定义一个切面类(LogAspect)
切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行

切面类里的方法: 通知方法 – 增强
前置通知:@Before
后置通知:@After 目标方法运行结束之后运行(无论方法正常结束还是异常结束)
返回通知:@AfterReturning 目标方法正常返回之后运行
异常通知:@AfterThrowing 在目标方法出现异常以后运行
环绕通知:@Around 动态代理,手动推进目标方法运行 (joinPoint.procced())

需要给切面类的目标方法标注何时何地运行

public class LogAspects {

   // @Before("public int com.atguigu.aop.MathCalculator.div(int,int)") 
   @Before("execution(public int com.atguigu.aop.MathCalculator.div(int,int))")
    public void logStart(){
        System.out.println("除法运行了,,,,,参数列表是:{}");
    }
   // @After("public int com.atguigu.aop.MathCalculator.*(..)")
   @After("execution(public int com.atguigu.aop.MathCalculator.*(..))")
    public void logEnd(){
        System.out.println("除法结束了,,,,, ");
    }
    @AfterReturning("public int com.atguigu.aop.MathCalculator.*(..)")
    public void logReturn(){
        System.out.println("除法正常返回.....运行结果是:{}");
    }
    @AfterThrowing("public int com.atguigu.aop.MathCalculator.*(..)")
    public void logException(){      
        System.out.println("除法异常.......异常信息是:{}");
    }
    /**
     * 这些方法方法都可以放入JoinPoint 作为参数,用来获取信息,
     * 注意:  如果传入多个参数时,JoinPoint一定要放在第一位!!
     */
     
}

这些方法方法都可以放入JoinPoint 作为参数,用来获取信息, 注意: 如果传入多个参数时,JoinPoint一定要放在第一位!!
上面每次写目标方法的切入点时都写这么长重复的代码太麻烦,可以将目标方法抽取出来:

@Pointcut("execution(public int com.atguigu.aop.MathCalculator.*(..))")
    public void  pointCut(){}

然后每次指定目标方法的切入点时可以这么写了⬇

    @AfterReturning("pointCut()")  //本类中的写法
    public void logReturn(){
        System.out.println("除法正常返回.....运行结果是:{}");
    }
    @AfterThrowing("com.atguigu.aop.LogAspects.pointCut")    //非本类的写法
    public void logException(){
        System.out.println("除法异常.......异常信息是:{}");
    }

step 3 : 将切面类和业务逻辑类(目标方法所在类)都加入到容器中

@Configuration
public class AopConfig {
    @Bean
    public MathCalculator mathCalculator(){
        return new MathCalculator();
    }
    @Bean
    public LogAspects logAspects() {
        return new LogAspects();
    }
}

step 4 : 给切面类添加@Aspect告诉spring容器哪个是切面类
step 5 : 配置类中添加@EnableAspectJAutoProxy,开启基于注解的aop模式
相当于在xml配置文件中写入

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

总结 :

  1. 将业务逻辑组件和切面类都加入到容器中;告诉是Spring哪个是切面类(@Aspect)
  2. 在切面类的每一个通知方法上标注通知注解,告诉Spring何时何地运行
  3. 开启基于注解的aop模式: @EnableAspectJAutoProxy

11. 声明式事务

11.1 环境搭建

  1. 导入相关依赖: 数据源、数据库驱动、Spring-jdbc模块
  2. 配置数据源、JdbcTemplate(spring提供的简化数据库操作的工具)

11.2 在方法上标注@Transactional

表示这是一个事务方法

11.3 使用@EnableTransactionManagement开启基于注解的事务管理功能

11.4 配置事务管理器来控制事务

 @Bean
    public PlatformTransactionManager transactionManager() throws Exception {
        return new DataSourceTransactionManager(dataSource());
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值