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. 组件导入
给容器中注册组件的方式
- 包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)-----
有局限性,不能导入第三方的组件
@Bean
导入的第三方包里面的组件@Import
快速给容器中导入一个组件
- @Import (要导入到容器中的组件) : 容器中就会自动注册这个组件,id默认是全类名;
- ImportSelector选择器: 返回需要导入的数组的全类名数组;
- ImportBeanDefinitionRegistrar:手动注册
- 使用Spring提供的FactoryBean(工厂bean)
- 默认获取到的是工厂bean调用getObject创建的对象
- 要获取工厂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容器找那个各个组件的依赖关系赋值;
- @Autowired默认优先按照类型去容器中找对应的组件:applicationContext.getBean(Xxx.class);
- 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找:applicationContext.getBean(“xxx”);
- @Qualifier(“xxx”): 使用@Qualifier指定需要装配的组件的id,而不是使用属性名
- 自动装配默认一定要讲属性赋好值,没有就会报错;
可以使用@Autowired(required=false);- @Primary: 让Spring进行自动装配的时候,默认使用首选的bean;
也可以继续使用@Qualifier指定需装配的bean的名字;
8.2 @Resource(JSR-250)注解和@Inject(JSR-330)
- @Resource: 可以和@Autowired一样实现自动装配注解,默认按照组件名称进行装配;
没有支持@Primary功能; 没有required的属性;- @Inject : 需要导入javax.Inject的包,和@Autowired的功能一样,没有required属性;
8.3 @Autowired注解的标注位置
@Autowired 注解可以放在构造器,参数, 方法, 属性 上, 都是从容器中获取参数组件的值;
- 标注在方法位置上: @Bean + 方法参数:参数从容器中获取;默认不写@Autowired效果一样;都能自动注入(赋值);
- 标注在构造器上: 如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取;
- 标注在参数位置上:
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>
总结 :
- 将业务逻辑组件和切面类都加入到容器中;告诉是Spring哪个是切面类(
@Aspect
)- 在切面类的每一个通知方法上标注通知注解,告诉Spring何时何地运行
- 开启基于注解的aop模式:
@EnableAspectJAutoProxy
11. 声明式事务
11.1 环境搭建
- 导入相关依赖: 数据源、数据库驱动、Spring-jdbc模块
- 配置数据源、JdbcTemplate(spring提供的简化数据库操作的工具)
11.2 在方法上标注@Transactional
表示这是一个事务方法
11.3 使用@EnableTransactionManagement开启基于注解的事务管理功能
11.4 配置事务管理器来控制事务
@Bean
public PlatformTransactionManager transactionManager() throws Exception {
return new DataSourceTransactionManager(dataSource());
}