Spring从入门到精通,AOP和IOC,JDK和CGlib动态代理,springBean的生命周期

1.spring介绍

Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。

1.1 spring 特点

1.方便解耦,简化开发
2.AOP编程的支持
3.声明式事务的支持
4.方便程序的测试
5.方便集成各种优秀框架
6.降低Java EE API的使用难度

1.2 spring组织架构

在这里插入图片描述

1.4 spring的核心模块

  • spring-core:依赖注入IOC与DI的最基本实现
  • spring-beans:Bean工厂与bean的装配
  • spring-context:spring的context上下文即IoC容器
  • spring-context-support
  • spring-expression:spring表达式语言

2.spring中的IOC

IOC是 Inverse of Control 的简写,意思是控制反转。是降低对象之间的耦合关系的设计思想。
DI是Dependency Injection的缩写,意思是依赖注入,说的是创建对象实例时,同时为这个对象注入它
所依赖的属性

2.1. 实现过程

步骤1:添加jar包

<!-- Spring的核心工具包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!--在基础IOC功能上提供扩展服务,还提供许多企业级服务的支持,有邮件服务、
任务调度、远程访问、缓存以及多种视图层框架的支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- Spring IOC的基础实现,包含访问配置文件、创建和管理bean等 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- Spring context的扩展支持,用于MVC方面 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- Spring表达式语言 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>

步骤2:创建配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

步骤3:在配置文件中创建对象

<bean id="对象名" class="类的完整路径">
<property name="属性名" ref="对象的id值"></property>
</bean>

步骤4:加载配置文件,获得对象

ApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");
Users users=(Users)app.getBean("u1");

2.2 bean标签的属性介绍

在这里插入图片描述

2.3 对象创建的方式

(1)无参构造
(2)有参构造

public Person(String name , Car car){
this.name = name;
this.car = car;
System.out.println("Person的有参构造方法:"+name+car);
}
<bean name="person" class="com.zzuli.spring.bean.Person">
<constructor-arg name="name" value="rose"/>
<constructor-arg name="car" ref="car"/>
</bean>

(3)静态方法创建对象

2.4 springBean的生命周期

在这里插入图片描述
Bean 生命周期的整个执行过程描述如下
1)根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean。

2)利用依赖注入完成 Bean 中所有属性值的配置注入。

3)如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前Bean 的 id 值。

4)如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。

5)如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。

6)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的预初始化方法postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。

7)如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。初始化bean的时候执行,可以针对某个具体的bean进行配置。afterPropertiesSet 必须实现 InitializingBean接口。实现 InitializingBean接口必须实现afterPropertiesSet方法。

8)如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。

9)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。

10)如果在 中指定了该 Bean 的作用范围为 scope=“singleton”,则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用范围为scope=“prototype”,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。

11)如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。

3.DI注入值

分类:一种是调取属性的set方法赋值,第二种使用构造方法赋值

3.1 set注入值

3.1.1 基本属性类型值注入

<property name="name" value="jeck" />

3.1.2 引用属性类型值注入

<property name="car" ref="car"></property>

3.2 构造注入:

3.2.1 可以通过name属性,按照参数名赋值
public Person(String name , Car car){
this.name = name;
this.car = car;
System.out.println("Person的有参构造方法:"+name+car);
}
<bean name="person" class="com.zzuli.spring.bean.Person">
<constructor-arg name="name" value="rose"/>
<constructor-arg name="car" ref="car"/>
</bean>
3.2.2 可以通过index属性,按照参数索引注入
<bean name="person2" class="com.zzuli.spring.bean.Person">
<constructor-arg name="name" value="helen" index="0"></constructor-arg>
<constructor-arg name="car" ref="car" index="1"></constructor-arg>
</bean>
3.2.3 使用type注入
<bean name="person2" class="com.zzuli.spring.bean.Person">
<constructor-arg name="name" value="988" type="java.lang.Integer"></constructor-arg>
<constructor-arg name="car" ref="car" ></constructor-arg>
</bean>

3.3 spel spring表达式 ***

<bean name="car" class="com.zzuli.spring.bean.Car" >
<property name="name" value="mime" />
<property name="color" value="白色"/>
</bean>
<!--利用spel引入car的属性 -->
<bean name="person1" class="com.zzuli.spring.bean.Person" p:car-ref="car">
<property name="name" value="#{car.name}"/>
<property name="age" value="#{person.age}"/>
</bean>

3.4 p命名空间注入值

使用p:属性名 完成注入,走set方法

  • 基本类型值: p:属性名=“值”
  • 引用类型值: P:属性名-ref=“bean名称”
  • 实现步骤:配置文件中 添加命名空间p
    xmlns:p=“http://www.springframework.org/schema/p”
<bean id="u6" class="com.entity.Users" p:age="30" p:name="李四" p:student-ref="stu1">
</bean>

3.5 复杂类型注入:Object[],list,set,map,java.util.Properties

 <!-- 数组变量注入 -->
<property name="arrs">
<list>
<value>数组1</value>
<!--引入其他类型-->
<ref bean="car"/>
</list>
</property>
<!-- 集合变量赋值-->
<property name="list">
<list>
<value>集合1</value>
<!--集合变量内部包含集合-->
<list>
<value>集合中的集合1</value>
<value>集合中的集合2</value>
<value>集合中的集合3</value>
</list>
<ref bean="car" />
</list>
</property>
<!--map赋值 -->
<property name="map">
<map>
<entry key="car" value-ref="car" />
<entry key="name" value="保时捷" />
<entry key="age" value="11"/>
</map>
</property>
<!-- properties赋值 -->
<property name="properties">
<props>
<prop key="name">pro1</prop>
<prop key="age">111</prop>
</props>
</property>

3.6 自动注入autowire(由程序自动给属性赋值)

autowire:
no 不自动装配(默认值)
byName 属性名=id名 ,调取set方法赋值
byType 属性的类型和id对象的类型相同,当找到多个同类型的对象时报错,调取set方法赋值
constructor 构造方法的参数类型和id对象的类型相同,当没有找到时,报错。调取构造方法赋值

<beans default-autowire="constructor/byName/byType/no">

4.注解实现IOC

(1)配置文件中添加约束

参考文件位置:

xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

(2)配置注解扫描:指定扫描包下所有类中的注解,扫描包时,会扫描包所有的子孙包

<!--扫描包设置-->
<context:component-scan base-package="com.xzk.spring.bean"></context:componentscan>

(3)注解

3.1 添加在类名上
@Component("对象名")
@Service("person") // service层
@Controller("person") // controller层
@Repository("person") // dao层
@Scope(scopeName="singleton") //单例对象
@Scope(scopeName="prototype") //多例对象
3.2 添加在属性上:
@Value("属性值")
private String name;
@Autowired //一个接口类型,同时有两个实现类,则报错,此时可以借助@Qualifier("beanname")
@Qualifier("bean name")
private Car car;
//说明:@Resource 是java的注释,但是Spring框架支持,@Resource指定注入哪个名称的对象
//@Resource(name="对象名") == @Autowired + @Qualifier("name")
@Resource(name="baoma")
private Car car;
3.3 添加在方法上
@PostConstruct //等价于init-method属性
public void init(){
System.out.println("初始化方法");
}
@PreDestroy //等价于destroy-method属性
public void destroy(){
System.out.println("销毁方法");
}

5.Aop介绍

AOP(Aspect Oriented Programming)即面向切面编程。即在不改变原程序的基础上为代码段增加新
的功能。应用在权限认证、日志、事务。
AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来
在这里插入图片描述

6.AOP的实现机制 *****

  • JDK 的动态代理:针对实现了接口的类产生代理。InvocationHandler接口
  • CGlib 的动态代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强的技术 生成当前类
    的子类对象,MethodInterceptor接口

在这里插入图片描述

6.1 JDK动态代理实现

  1. 创建接口和对应实现类
public interface UserService {
public void login();
}
public class UserServiceImpl implements UserService {
public void login(){}
}
  1. 创建动态代理类,实现InvocationHandler接口
public class agency implements InvocationHandler {
private UserService target; //目标对象
public agency(UserService target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
//本方法中的其他输出输入增强
//proxy 代理方法被调用的代理实例
System.out.println("方法触发了");
//执行被代理类 原方法
Object invoke = method.invoke(target, args);
System.out.println("执行完毕了");
return invoke;
}
}

测试

@Test
public void test1(){
//测试JDK动态代理技术
UserService us = new UserServiceImpl();
agency ag = new agency(us);
//这里不能转换成一个实际的类,必须是接口类型
UserService uservice = (UserService)
Proxy.newProxyInstance(us.getClass().getClassLoader(),us.getClass().getInterfaces(),ag);
uservice.login();
}

2.2 CGlib实现代理

使用JDK只能为接口创建代理实例.这一点可以从Proxy的接口方法
newProxyInstance(ClassLoader loader,Class [] interfaces,InvocarionHandler h)中看的很清楚

CGLib采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方
法的调用并顺势织入横切逻辑

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>

1.创建普通类
public class Users{
public void login(){}
}
2. 创建创建CGLib代理器

class CgProxy implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
System.out.println("输出语句1");
//参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法
//引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。
Object obj= methodProxy.invokeSuper(o,objects);
System.out.println("输出语句2");
return obj;
}
}

测试

public static void main(String[] args) {
//1.创建真实对象
Users users = new Users();
//2.创建代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(users.getClass());
enhancer.setCallback(new CglibProxy());
Users o = (Users) enhancer.create();//代理对象
o.login();
}
结论:spring同时使用了这两种方式,底层会自行判断应该使用哪种
两种代理方式的区别:

1、jdk动态代理生成的代理类和委托类实现了相同的接口;
2、cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字
修饰的方法;
3、jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;

7.spring中使用aop

<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>

(2)添加项目原有的调取过程
(3)创建增强类(本质上就是一个普通类)
//前置通知:目标方法运行之前调用 aop:before
//后置通知(如果出现异常不会调用):在目标方法运行之后调用 aop:after-returning
//环绕通知:在目标方法之前和之后都调用 aop:around
//最终通知(无论是否出现 异常都会调用):在目标方法运行之后调用 aop:after
//异常增强:程序出现异常时执行(要求:程序代码中不要处理异常) aop:after-throwing
如环绕增强:

public Object around(ProceedingJoinPoint point) throws Throwable{
point.proceed();
}

(4)添加aop命名空间
xmlns:aop=“http://www.springframework.org/schema/aop”
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
(5)设置配置文件

<!--1.创建目标类对象-->
<bean name="userService" class="com.zzuli.spring.service.UserServiceImpl" />
<!--2.配置增强类对象-->
<bean name="myAdvice" class="com.zzuli.spring.aop.MyAdivce" />
<!-- 3.配置将增强织入目标对象-->
<aop:config>
<aop:pointcut id="pc"
expression="execution(* com.zzuli.spring.service.ServiceImpl.*.*
(..))"/>
<aop:aspect ref="myAdvice">
<aop:before method="before" pointcut-ref="pc" />
<aop:after-returning method="afterReturning" pointcut-ref="pc"
/>
<aop:around method="around" pointcut-ref="pc" />
<aop:after-throwing method="afterException" pointcut-ref="pc"
/>
<aop:after method="after" pointcut-ref="pc" />
</aop:aspect>
</aop:config>

8.切入点方法的定义

  • public * addUser(com.pb.entity.User):“*”表示匹配所有类型的返回值。

  • public void (com.pb.entity.User):“”表示匹配所有方法名。

  • public void addUser (…):“…”表示匹配所有参数个数和类型。
    示例:

  • com.pb.service..(…):匹配com.pb.service 包下所有类的所有方法。
    示例:
  • com.pb.service…*(…):匹配com.pb.service 包及子包下所有类的所有方法

9.使用了AspectJ依赖注解开发

spring AOP的注解方式:
注:1)增强类也需要创建对象(使用@Component)
2)要启动扫描spring注解包的代码:

<context:component-scan base-package="com.xzk"></context:component-scan>

<1> 除了启动spring的注解之外 还要启动aspectJ的注解方式

<aop:aspectj-autoproxy/>

<2> 在切面类(增强类)上添加:@Aspect
<3> 定义一个任意方法

@Pointcut("execution(* com.*.*(..))")
public void anyMethod(){}

为什么要定义一个任意方法??? 因为@Pointcut 要求要放在一个方法

用法

@Pointcut("execution(* com.*.*(..))")
public void anyMethod(){}
@Before("anyMethod()")
public void log(JoinPoint){
System.out.println("myAspect....log....before");
}
@Around("anyMethod()")
public void aroundTest(ProceedingJoinPoint pjp){
System.out.println("around...before....");
try {
pjp.proceed();//执行目标方法
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("around...after....");
}

顺序问题
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值