Spring—IOC
什么是IOC
Inverse Of Controll
:控制反转
反转了依赖关系的满足方式
,由之前的自己创建依赖对象,变为由工厂推送。(变主动为被动,即反转)
解决了具有依赖关系的组件之间的强耦合,使得项目形态更加稳健
DI
Dependency Injection
:依赖注入
全新的依赖满足方式,体现在编码中就是全新的赋值方式 ==> 在工厂中为属性推送值
如:<property name="userDAO" ref="userDAO"></property>
IOC 和 DI
在spring中关于IOC和DI的描述是这样的:** IOC(DI)
,即,是一码事
IOC 是思想
:指导我们在满足依赖时,应该有反转的设计。
DI 是手段
:实际操作时,就是在一次次的 注入
Spring—AOP
什么是AOP?
Aspect-Oriented-Programming(面向切面编程),一种编程思想
切面:Aspect,由切入点和==额外功能(增强)==组成
作用:提供了新的编程角度,不在只是考虑 类,对象,而是考虑切面,切面和目标形成代理,解决项目业务中额外功能冗余的问题
业务中的问题(为什么要有AOP)
业务层中存在的问题:两类逻辑 = 核心业务 + 额外功能,其中而外功能中存在大量的代码冗余
使得项目在维护过程中存在极大的隐患(如果额外功能的代码需要废除【需求修改】,那么维护起来简直就是火葬场)
所以我们会想要把冗余的代码抽离出来,在需要的时候,去调用
class UserServiceImpl implements UserService{
private UserDAO ud;
public void updateUser(User user){
System.out.println("事务管理功能");//额外功能 冗余
ud.update(user); //核心功能
}
public void inserUser(User user){
System.out.println("事务管理功能");//额外功能 冗余
ud.insertUser(user);//核心功能
}
public User queryUser(Integer id){
System.out.println("事务管理功能");//额外功能 冗余
ud.queryUser(id);//核心功能
}
}
解决
静态代理
代理类打理了额外的功能
目标类: 处理业务逻辑的代码(被代理的类)
代理类的原则: 需要和原始的业务(target)实现同样的接口,保持功能一致
代理类的组成: 额外功能(Advice) + 目标(Target)
class UserServiceProxy implements UserService{//代理类
UserService us=new UserServiceImpl();
public void updateUser(User user){
System.out.println("事务管理功能"); //代理类负责额外功能
us.updateUser(user); // 目标自己负责核心功能
}
public void inserUser(User user){
System.out.println("事务管理功能");//代理类负责额外功能
us.insertUser(user);// 目标自己负责核心功能
}
public User queryUser(Integer id){
System.out.println("事务管理功能");//代理类负责额外功能
us.queryUser(user);// 目标自己负责核心功能
}
}
新问题:
虽然解决了目标类的问题,但是自身仍然存在大量的冗余
所以引出动态代理
动态代理
通过动态字节码技术,在运行时动态生成代理(反射)
则既不用维护代理类,又可以有代码打理的额外功能
动态代理的实现方案:
jdk代理(jdk在反射包中提供的一套api),通过和目标实现相同的接口保证功能一致
cglib代理(第三方cglib库中的一套api)通过集成目标保证功能一致
Spring的AOP,底层采纳了如上两种代理实现,并对动态代理提供了,简单的,可操作性强的解决方案
当被代理的类实现的有接口的化,使用jdk动态代理
当被代理的类实现的没有接口的情况下,使用chlib动态代理
AOP的编码流程
模拟
1.导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
2.准备Target【目标】
public class UserServiceImpl implements UserService{
private UserDAO userDAO;
// set/get...
@Override
public void updateUser(User user) {
System.out.println("update in service===============");
userDAO.updateUser(user);
}
@Override
public void insertUser(User user) {
System.out.println("insert in service===============");
userDAO.insertUser(user);
}
}
3.准备Advice【额外功能】
public class MyBeforeAdvice implements MethodBeforeAdvice{
/**
* @param method 当前执行的方法
* @param args 当前执行的方法中的参数
* @param target 目标对象
* @throws Throwable
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("before~~~");
}
}
4.编织Weave
所谓的编织,即,将Target和Advice组装成代理
当然组装过程由Spring管理,只需要做出相应的配置,告诉Spring需要组装谁即可
<!-- 声明 Target + Advice -->
<!-- 声明 Target -->
<bean id="userService" class="com.zhj.service.UserServiceImpl">
<!-- 为userDAO属性赋值,值为id=“userDAO”的组件 -->
<property name="userDAO" ref="userDAO"/>
</bean>
<!-- Advice -->
<bean id="myBefore" class="com.zhj.advice.MyBeforeAdvice"/>
<!-- 编织 配置 -->
<aop:config>
<!-- 切入点=pointcut
execution()表达式:描述切入位置
组成:修饰符 返回值 包 类 方法名 参数表
public Integer com.xx.xxx.AA.xxxXXX(int,String)
* com.service.UserServiceImpl.*(..):com.service包下UserServiceImpl类中,返回值修饰符任意,方法名任意,
参数表任意
* com.service.UserServiceImpl.queryUser(..):同上,只是方法名不是任意,而是 ”queryUser“
-->
<aop:pointcut id="pc" expression="execution(* com.service.UserServiceImpl.queryUser(..))"/>
<aop:advisor advice-ref="myBefore" pointcut-ref="pc"/>
</aop:config>
编织过程:
1.导入schema,关于aop的配置导入,因为寻要用到config标签,模仿beans的配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
2.声明目标
<bean id="userService" class="com.qf.service.impl.UserServiceImpl"></bean>
3.声明额外功能
<bean id="logbefore" class="com.qf.advice.MyLogAdvice"></bean>
4.编织,先定义切入点,通过目标的信息,额外功能的信息,组成一个新的类:Proxy
<!--
编织
通过目标的信息,额外功能的信息,组成一个新的类:Proxy
-->
<aop:config>
<!-- 切入点:目标中的方法
execution表达式
execution(修饰符,返回值,包.类.方法名(参数表))
修饰符,返回值一般都写 *,
execution(public Integer com.qf.service.impl.UserServiceImpl.queryById(Integer))
execution(* com.qf.service.impl.UserServiceImpl.*(..))
-->
<aop:pointcut id="pc04" expression="execution(* com.qf.service.impl.UserServiceImpl.*(..))"></aop:pointcut>
<!--将某个额外功能,编织到某个切入点中-->
<aop:advisor advice-ref="logbefore" pointcut-ref="pc04"></aop:advisor>
</aop:config>
多种Advice(额外方法)
1.前置额外功能
实现MethodBeforeAdvice接口
public class MyBeforeAdvice implements MethodBeforeAdvice{
/**
* @param method 当前执行的方法
* @param args 当前执行的方法中的参数
* @param target 目标对象
* @throws Throwable
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("before~~~");
}
}
2.后置额外功能
实现AfterReturningAdvice
public class MyAfterAdvice implements AfterReturningAdvice{
/**
*
* @param returnValue 目标业务方法返回值
* @param method 当前执行的业务方法对象
* @param args 方法的参数
* @param target 目标对象
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("after~~~");
}
}
3.环绕额外功能
实现MethodInterceptor接口
public class MyMethodInterceptor implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("begin~~");
Object ret = invocation.proceed();//执行目标业务方法
System.out.println("end~~");
return ret;//返回目标业务方法返回值
}
}
注意:
如果同时使用前置,后置,环绕额外功能,那么顺序是前置,环绕,后置
异常额外功能
实现ThrowsAdvice接口
public class MyThrows implements ThrowsAdvice{
//目标业务方法中抛出异常时,执行此方法。ex=抛出的异常对象
public void afterThrowing(Exception ex){
System.out.println(ex.getMessage()+"~~~");
}
}
编织
<!-- 声明 target+advice -->
<bean id="userService" class="xxxxx"></bean>
<bean id="myXXAdvice" class="xxxxx"></bean>
<aop:config>
<aop:pointcut id="pc" expression="execution(* com..UserService*.*(..))"/>
<aop:advisor advice-ref="advice的BeanId" pointcut-ref="pc"/>
</aop:config>
切入顺序
<!-- order值越小,切入顺序就越优先 -->
<aop:advisor advice-ref="before05" pointcut-ref="pc05" order="2"/>
<aop:advisor advice-ref="before04" pointcut-ref="pc05" order="1"/>
切入点表达式
execution(可以具体到某个方法)
由 【修饰符 返回值 包.类.方法 参数表】 组成
1> * com.service.UserServiceImpl.queryUser(..)
修饰符:任意
返回值:任意
包:com.service
类:UserServiceImpl
方法:queryUser
参数表:任意
2> * com.service.UserServiceImpl.*(..)
修饰符:任意
返回值:任意
包:com.service
类:UserServiceImpl
方法:所有,任意
参数表:任意
3> * com..UserServiceImpl.*(..)
修饰符:任意
返回值:任意
包:com包,及其子包
类:UserServiceImpl
方法:所有,任意
参数表:任意
4> * com.service.*.*(..)
修饰符:任意
返回值:任意
包:com.service
类:所有,任意
方法:所有,任意
参数表:任意
5> * *(..) 不建议
修饰符:任意
返回值:任意
包:任意
类:所有,任意
方法:所有,任意
参数表:任意
6> * com.service.UserServiceImpl.query*(..) 【技巧:批量切入】
修饰符:任意
返回值:任意
包:com.service
类:UserServiceImpl
方法:所有,任意
参数表:任意
*注意:尽量精确,避免不必要的切入
within(类中的方法)
描述包和类,类中的方法都切入
within(com.service.UserServiceImpl) 类中的所有方法
within(com..UserServiceImpl) com包和com子包下的类中的所有方法
<aop:pointcut id="pc" expression="within(com..UserServiceImpl)"/>
args
描述参数表,符合的方法都切入
args(int,String,com.entity.User) 参数表如此的方法
<aop:pointcut id="pc" expression="args(int,String,com.entity.User)"/>
联用
不同种类的表达式之间,可以使用逻辑运算
and or not
<aop:pointcut id="pc" expression="execution(* com.zhj.service.UserServiceImpl.*(..)) and args(com.User)"/>
<aop:pointcut id="pc" expression="within(com.service.UserServiceImpl) or args(com.User)"/>
<aop:pointcut id="pc" expression="within(com.service.UserServiceImpl) and not args(com.User)"/>
JDK代理(必须实现接口)
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import service.UserService;
import tx.TransactionManager;
public class DynamicProxy {
//创建代理对象 需要传入真实对象和事务对象
public static Object getProxy(final UserService target,final TransactionManager tx){
/**
* loader 真实对象的类加载器
* interfaces 真实对象的接口
* h
* 问题:是否能够不传接口?? 必须要求传入接口
*/
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
//当代理对象调用方法时才会执行invoke操作
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
tx.begin(); //事务开始
Object result = method.invoke(target, args);//调用目标方法
tx.commit();
return result;
}
}
);
return proxy;
}
}
cglib代理(可以不实现接口)
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import service.UserService;
import tx.TransactionManager;
public class DynamicProxy {
//创建代理对象 需要传入真实对象和事务对象
public static Object getProxy(final UserService target,final TransactionManager tx){
//1.创建增强器 底层实现是通过二进制码的方式
Enhancer enhancer = new Enhancer();
//2.设置接口
enhancer.setInterfaces(target.getClass().getInterfaces());
//3.设置父类 cgLib创建的代理对象都是目标对象的子类
enhancer.setSuperclass(target.getClass());
//4.设置回调
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
tx.begin();
//通过目标对象调用方法
Object result = method.invoke(target, args);
tx.commit();
return result;
}
});
//获取代理对象
return enhancer.create();
}
}
cglib选择
<!-- 强制使用cglib代理 -->
<aop:config proxy-target-class="true">