用来讲解的例子:在main函数中创建spring容器
用AnnotationConfigApplicationContext创建一个spring容器
// 创建一个Spring容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();
配置类AppConfig :这里是AnnotationConfigApplicationContext扫描的类,主要是用来添加项目的一些信息
@ComponentScan("com.zhouyu")
public class AppConfig {
}
其中这个例子用到的对象bean:
UserService 对象:
@Component
public class UserService {
@Autowired
private OrderService orderService;
public void test(){
System.out.println(orderService);
}
}
OrderService 对象:
@Component
public class OrderService {
}
01.属于spring特性,依赖注入:
依赖注入 @Autowird
例子:UserService 类中有属性orderService,被 @Autowird修饰
@Component
public class UserService {
@Autowired
private OrderService orderService;
public void test(){
System.out.println(orderService);
}
}
测试:
两种UserService 类的对象实例化,前一种是用spring来创建的,后一种是new创建的
UserService userService1 = (UserService) applicationContext.getBean("userService");
UserService userService2= new UserService ();
spring依赖注入的原理:
//利用class的反射,获取class对象中的全部属性
for (Field field : userService1.getClass().getDeclaredFields()) {
//在获取的属性中去判断是不是有autowired的注解,有的话,就会执行属性赋值的操作,这也就是依赖注入
if (field.isAnnotationPresent(Autowired.class)) {
field.set(userService1, ??);
}
}
结果:
由spring创建的bean,属性orderService有值
new 出来的对象 属性orderService没有值
02.spring创建自动对象,一般用类的无参构造方法。
讨论在spring创建类的实例的时候,会调用到类的什么构造函数:
Spring的判断逻辑如下:
- 如果一个类只存在一个构造方法,不管该构造方法是无参构造方法,还是有参构造
方法,Spring都会用这个构造方法(有参的构造函数,参数要在spring容器中去按照参数类型来找,如果同一个类型存在多个bean对象的话,在按照参数的名字取去找) - 如果一个类存在多个构造方法
a. 这些构造方法中,存在一个无参的构造方法,那么Spring就会用
这个无参的构造方法
b. 这些构造方法中,不存在一个无参的构造方法,那么Spring就会
报错
例子:
此时userservice类只有一个有参的构造函数,参数类型是OrderService 参数名是orderService123
在配置类AppConfig中,同时创建两个UserService的bean,
此时,由于@Component 和@Bean ,一共是有三个UserService 的bean的
@Component
public class UserService {
private OrderService orderService;
public UserService(OrderService orderService123){
this.orderService=orderService123;
System.out.println("2");
}
}
@ComponentScan("com.zhouyu")
public class AppConfig {
@Bean
public UserService userService1 () {
return new UserService ();
}
@Bean
public UserService userService2 () {
return new UserService ();
}
}
测试:此时spring容器中已经有三个OrderService类型的对象了
一.如果 UserService 类的那个有参构造函数 的参数 类型 OrderService 并没有被spring创建bean对象的话,也就是一个普通类的话(没有被@Component 修饰的话),报错
二.如果spring之前已经把这个类型实例化对象了,就会去spring容器内取找,查找的顺序是,先按照参数类型来找,如果满足的bean数量超过一个的话,而后根据参数名字来找,如果都找不到的话,就会报错
三.但是如果去掉配置类中的两个@bean的话,只留下@Component OrderService的话,此时spring容器只剩下一个bean ,无论构造函数中的OrderService参数名叫什么,都可以成功把参数赋值到这个spring创建的bean
四.如果执意要指定那个构造函数是spring默认的用来自动创建bean的,添加@Autowired到构造函数上
03.spring把类依赖注入后,开始初始化
初始化有三个阶段:
初始化前,初始化,初始化后
初始化前一般调用 @PostConstruct 修饰的方法
例子:UserService类
其中有被 @PostConstruct修饰的方法a
@Component
public class UserService {
@Autowired
private OrderService orderService;
public void test(){
System.out.println(orderService);
}
@PostConstruct
public void a(){
System.out.println("这个是初始化前的操作");
}
}
测试:
在用getBean来实例化UserService的bean对象的时候,用在显示台输出内容:这个是初始化前的操作
// 创建一个Spring容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();
原理
//是在实例化的过程中,用Class类的反射的机制,来获取类的全部方法
for (Method method : userService1.getClass().getDeclaredMethods()) {
//在获取的方法中去判断是不是有注解PostConstruct,有的话,会被唤醒(这里是JDK的动态代理method.invoke 是用于执行method的方法)
if (method.isAnnotationPresent(PostConstruct.class)) {
method.invoke(userService1, null);
}
}
04 初始化
初始化,是实现一个接口 InitializingBean
在实现这个接口的过程中,会重写afterPropertiesSet方法,这个方法就会在初始化的这个阶段来执行,就像上文中写的那样被@PostContruct 修饰的方法,会在初始化前被调用
@Component
public class UserService implements InitializingBean{
@Autowired
private OrderService orderService;
public void test(){
System.out.println(orderService);
}
@PostConstruct
public void a(){
System.out.println("这个是初始化前的操作");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("这个是初始化的操作");
}
}
执行这个afterPropertiesSet方法的原理:
用instanceof 方法 判断是不是实现了接口InitializingBean,如果是的,就会调用afterPropertiesSet
InitializingBean接口的详细情况:
InitializingBean接口源码中的情况:
invokeInitMethods方法会判断是不是实施了接口InitializingBean,
如果有,调用重写的方法afterPropertiesSet:
05.初始化后,这里是spring的AOP的操作地点
AOP的原理:
配置类要添加一个注解
@ComponentScan("com.zhouyu")
@EnableAspectAutoProxy
public class AppConfig {
}
创建一个切面类:这里面定义的是在哪里去插入切面逻辑
@Aspect
@Component
public class ZhouyuAspect {
//切入点:userservice的test方法
@Pointcut("execution(public void com.zhouyu.service.UserService.test())")
public void a(){
}
@Before("a()")
public void zhouyuBefore(JoinPoint joinPoint) {
System.out.println("zhouyuBefore");
}
}
被切入的类的代码
@Component
public class UserService {
@Autowired
private OrderService orderService;
public void test(){
System.out.println(orderService);
}
}
此时,运行到切入点的时候(也就是UserService类的test方法),UserService类的对象会变成一个代理类对象
但是要注意的是:
此时这个代理对象的属性OrderService是空的
原理是:AOP生成代理类对象的时候,并没有进行依赖注入,也就没有属性值
但是运行到这个test方法时候,断点会跳转到UserServicenei进去去看,这个时候,属性OrderService是有值的
原理:
1.cjlib会生成一个代理类,这个代理类会继承被代理对象类,这里的target会被赋值给被代理类的对象,也就是切入点所在的类的对象(此时,这个对象应该是spring中创建的bean)
2.并产生一个对象(代理类实例)
3. 这个实例会执行代理类的方法(也就是增添切面逻辑的test方法)
增添切面逻辑的test方法
查看效果:此时的代理类的target属性:
这个一个例子:如何判断当前Bean对象需不需要进行AOP:
找出所有的切面Bean
遍历切面中的每个方法,看是否写了@Before、@After等注解
如果写了,则判断所对应的Pointcut是否和当前Bean对象的类是否匹配
如果匹配则表示当前Bean对象有匹配的的Pointcut,表示需要进行AOP
08.数据库的事务
这里两个方法用到了datasource方法,不是bean这样的使用,没有autowird 会创建两个datasource
@configuration 会运行代理模式 会产生一个AppConfig的代理对象
这个代理对象会在spring的容器先找bean,datasource此时已经创建了bean对象,这个bean对象 会自动注入 那两个
09.spring事务管理