Spring—IOC、AOP

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">
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值