Spring 的 AOP 部分

 3.1 AOP简介【常见面试】
     AOP:全称:Aspect Oreinted Programming:面向切面编程
     OOP:全称:Object Oreinted Programming:面向对象编程
     Aop 含义:Aop 和 OOP 不存在谁取代谁的关系,它们是相互促进,相互补充,Aop 是在不改变原来 OOP 类代码的基础之上,对原来类的功能进行拓展。
     AOP 作用:解决了软件工程中的关注点分离问题。可以让系统变得高内聚、低耦合、便于项目后期的维护和拓展。
     Aop 底层原理:动态代理。
 
  3.2 问题提出
     需求:我们现在需要实现一个计算器功能,可以进行加、减、乘、除运算。还需要在加减乘除之前或者之后打印一条日志信息。
      具体实现:
        基本实现:让项目后期变得难以维护。
        动态代理:
        AOP 编程:
 
  3.3 AOP中的几个概念
     通知[Advice]:表示拓展的功能,本质:方法
     切面[Aspect]:通知所在的类,称之为切面,本质是一个类
     切入点[PointCut]:表示通知对谁进行拓展,本质:表达式
     连接点[JoinPoint]:通知和目标方法的交点
     目标对象[Target]:被拓展的类对象,称为目标对象
     织入[Weaving]:将通知应用到目标方法的过程
 
  3.4 AOP对于问题的实现
    第一步:创建动态 web 工程,导入 jar 包
     
                    第二步:需要将被拓展的类和拓展的类加入到容器中:扫描包+注解
     第三步:在 spring 配置文件中开启基于注解的切面支持及在拓展的类上加@Aspect 注解
     第四步:在拓展的类的方法上指定切入点表达式
 
    applicationContext.xml:
        
<context:component-scan base-package="com.atguigu"/>
<!--开启基于注解的切面支持
-->
<aop:aspectj-autoproxy/>
 
             实现类 CaculatorImpl:
        
 
@Component
public class CaculatorImpl implements Caculator {
//签名:包名+类名+方法名(参数列表)
@Override
public int add(int i, int j) {
int result = i+j;
System.out.println("add方法执行了......");
return
result;
}
@Override
public int
substract(int i, int j) {
int result = i-j;
return
result;
}
@Override
public int
multiply(int i, int j) {
int result = i*j;
return
result;
}
@Override
public int
divide(int i, int j) {
int result = i/j;
return result;
}
}
    切面类:
@Component
@Aspect //该注解标记的类表示当前类是一个切面类
public class LogAspect {
@Before(value = "execution(public int com.atguigu.aop.CaculatorImpl.add(int,int))")
public void beforeLog(){
System.out.println("在目标方法执行之前打印........");
}
@After(value = "execution(public int com.atguigu.aop.CaculatorImpl.add(int,int))")
public void afterLog(){
System.out.println("在目标方法执行之后打印........");
}
}
 
 
   第五步:测试
  
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Caculator bean = context.getBean(Caculator.class);
bean.add(3,5);
}
  3.5 图解 AOP 中涉及到的几个概念
     
 
  3.6 AOP底层原理
     
 
  3.7 代理模式
    静态代理:指的只能代理某一个接口的实现类对象
   Payment:
public interface Payment {
public void pay(double money);
}
        真实用户 RealPayment:
public class RealPayment implements Payment{
@Override
public void pay(double money) {
System.out.println("作为真实用户,我们只关注付了"+money+"钱");
}
}
 
 
            支付宝代理 AliPay:
 
public class AliPay
implements Payment {
private Payment
payment;
public AliPay(Payment payment){
this.payment=payment;
}
public void beforePay(){
System.out.println("支付宝代理真实用户把钱从银行取出来....");
}
@Override
public void pay(double money) {
beforePay();
this.payment.pay(money);
afterPay();
}
public void afterPay(){
System.out.println("支付宝代理真实用户把钱付给商家....");
}
}
 
 
 
         测试类:
public void testPay(){
AliPay aliPay = new AliPay(new RealPayment());
aliPay.pay(32.9);
}
 
注意:静态代理只能代理某个特定接口的实现类对象
 
 
        WeiPayImpl 实现类:
public class WeiPayImpl implements WeiPay {
@Override
public void pay(double money) {
System.out.println("微信付款"+money);
}
}
 
   动态代理:指的只能代理任意接口的实现类对象
 
    测试类:
//万能动态代理
@Test
public void test02(){
// RealPayment realPayment = new RealPayment();
WeiPayImpl weiPay = new WeiPayImpl();
/************************************************************
newProxyInstance()参数:
第一个参数:被代理类的类加载器
第二个参数:被代理类实现的接口
第三个参数:
InvocationHandler接口的实现类对象,咱们可以在InvocationHandler
接口的实现类中对被代理类进行拓展。
newProxyInstance()返回值:表示代理类对象
************************************************************/
MyInvocationHandler<WeiPay> result = new MyInvocationHandler<WeiPay>(weiPay);
WeiPay proxy = result.proxy();
proxy.pay(32.8);
}
 
    InvocationHandler 实现类:
public class MyInvocationHandler<T> implements InvocationHandler {
private T target;
public MyInvocationHandler(T target){
this.target = target;
}
public T proxy(){
T o = (T)Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
return o;
}
//method:表示被代理类的目标方法,
//args:当调用被代理类方法的时候传递的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
System.out.println("把钱从银行取出来...");
//调用被代理类的目标方法
Object result = method.invoke(target, args);
System.out.println("把钱付给商家...");
return result;
}
}
 
两者区别:
相同点:
1. 被代理类和代理类都实现相同的接口
2. 都是在不改变被代理类代码的基础上对被代理类功能进行拓展
不同点:
1. 静态代理只能代理某个接口的实现类对象
2. 动态代理可以代理任意接口的实现类对象。
 
 
 3.8 切面中的五种通知
    前置通知[@Before]:在目标方法执行之前执行的功能。
    后置通知[@After]:无论目标方法是否执行成功,在目标方法执行之后,后置通知都会执行。
    返回通知[@AfterReturning]:目标方法执行成功之后,返回通知才会执行。
    异常通知[@AfterThrowing]:目标方法在执行出现异常的时候,异常通知才会执行。
    环绕通知[@Around]:以一抵四。
@Component
@Aspect //该注解标记的类表示当前类是一个切面类
public class LogAspect {
    //前置通知
    @Before(value = "execution(public int com.atguigu.aop.CaculatorImpl.*(int,int))")
    public void beforeLog(){
        System.out.println("日志切面前置通知");
    }
 
    //后置通知
    @After(value = "execution(public int com.atguigu.aop.CaculatorImpl.*(int,int))")
    public void afterLog(){
        System.out.println("日志切面后置通知");
    }
 
    //返回通知
    @AfterReturning(value = "execution(public int com.atguigu.aop.CaculatorImpl.*(int,int))")
    public void afterReturningLog(){
        System.out.println("日志切面 返回通知");
    }
 
    //异常通知
    @AfterThrowing(value = "execution(public int com.atguigu.aop.CaculatorImpl.*(int,int))")
    public void afterThrowingLog(){
        System.out.println("日志切面 异常通知");
    }
}
 
 
  2.9 通知的底层结构
 
    //spring 4.x版本 底层结构
try{
    try{
        //1.前置通知
        //目标方法
    }finally {
        //2.后置通知
    }
    //3.返回通知
}catch (Throwable ex){
    //4.异常通知
}
   //spring 5.x版本 底层结构 
try{
    //1.前置通知
      //目标方法
    //2.返回通知
}catch (Throwable ex){
    //3.异常通知
}finally {
    //4.后置通知
}
 
  3.10 切入点表达式
    作用:用来确定对谁进行拓展
    语法:execution([权限修饰符] [返回值类型] [全类名] [参数列表])
    最简洁[最模糊]:
execution(* *.*(..))
    最复杂[最精确]:
execution(public int com.atguigu.CaculatorImpl.add(int,int))
 
    切入点表达式重用:
@Component
@Aspect //该注解标记的类表示当前类是一个切面类
public class LogAspect {
   @Pointcut(value = "execution(public int com.atguigu.aop.CaculatorImpl.*(..))")
   public void myPointCut(){}
   //前置通知
   @Before(value = "myPointCut()")
   public void beforeLog(){
     System.out.println("日志切面 前置通知");
   }
}
切入点表达式支持 与、或、非操作:
 
 
  3.11 从通知中获取目标方法信息
try{
    try{
        //1.前置通知: 获取目标方法名和传递给目标方法的参数值信息
        //目标方法:
    }finally {
        //2.后置通知
    }
    //3.返回通知: 获取目标方法的返回结果
}catch (Throwableex){
    //4.异常通知:获取目标方法的异常信息
}
 
前置通知:获取目标方法名和传递目标方法的参数值信息
//前置通知
@Before(value = "myPointCut()")
public void beforeLog(JoinPoint joinPoint){
    //方法签名:全类名.方法名(参数列表)
    //获取方法名
    String methodName = joinPoint.getSignature().getName();
    Object[] args = joinPoint.getArgs();
    System.out.println("日志切面  前置通知,目标方法名为:"+methodName+",参数值
为:"+ Arrays.asList(args));
}
返回通知:获取目标方法返回结果
//返回通知
//在返回通知中获取目标方法的返回结果,有两点要求:
    1:返回通知的参数类型最好是Object 
    2.方法的参数名必须和returning属性值保持一致
 
@AfterReturning(value = "myPointCut()",returning = "result")
public void afterReturningLog(Object result){
        System.out.println("日志切面返回通知,返回结果为:"+result);
}
 
异常通知:获取目标方法的异常信息
//异常通知
//在异常通知中获取目标方法的异常信息,有两点要求:
    1.要求参数的类型必须为 Throwing,
    2要求异常通知的参数名必须和@AfterThrowing注解的throwing属性值保持一致
@AfterThrowing(value = "myPointCut()",throwing = "ex")
public void afterThrowingLog(Throwable ex){
        System.out.println("日志切面异常通知,异常信息为:"+ex.getMessage());
}
  3.12 使用环绕通知实现事物切面
    编程式事务:
Connection conn=JdbcUtils.getConnection();
try{
    conn.setAutoCommit(false); //1.取消事务的自动提交功能
    //多条增删改 sql            // 2.执行多次增删改操作
    conn.commit();             //3.提交事务
}catch(Exception ex){
    conn.rollback()            //4.回滚操作
}finally{
    conn.close();              //5.关闭连接
}
 
@Component
@Aspect
public class TransactionAspect {
    /************************************************************
        环绕通知:
            1.要求环绕通知标记的方法返回值类型必须为Object类型
            2.要求将目标方法的返回值返回。
    ************************************************************/
    @Around(value = "com.atguigu.aop.LogAspect.myPointCut()")
    public Object around(ProceedingJoinPoint joinPoint){
        Object proceed = null;
        try {
            try{
                //1.前置通知
                System.out.println("事务切面前置通知");
                //放行请求:执行目标方法
                proceed = joinPoint.proceed();
            }finally {
                System.out.println("事务切面后置通知");
            }
            System.out.println("事务切面返回通知");
        } catch (Throwable throwable) {
            System.out.println("事务切面异常通知");
        }
        return proceed;
    }
}
 
 
  3.13 多切面的情况下,切面中的通知的执行顺序问题
try{
    try{
        //1.前置通知
        //目标方法
    }finally {
        //2.后置通知
    }
    //3.返回通知
}catch (Throwable ex){
    //4.异常通知
}
 
三种情况:
  • 当目标方法不出现异常的时候
  • 当目标方法出现异常的时候,内层切面不抛出异常
  • 当目标方法出现异常的时候,内层切面抛出异常
 
  3.14 多切面的情况下,切面的执行顺序
    多切面情况下,切面的执行顺序是由标记在切面类上的@Order 决定的,@Order 注解的value 属性值越小,切面的优先级越高。
 
  3.15 基于xml配置的AOP
<bean id="caculator" class="com.atguigu.aop.CaculatorImpl"></bean>
<bean id="logAspect" class="com.atguigu.aop.LogAspect"></bean>
<bean id="transactionAspect" class="com.atguigu.aop.TransactionAspect"></bean>
 
<aop:config>
    <aop:pointcut id="myPointCut" expression="execution(public int
com.atguigu.aop.CaculatorImpl.add(int, int))"/>
    <aop:aspect ref="logAspect" order="10">
        <aop:before method="beforeLog" pointcut-ref="myPointCut"></aop:before>
        <aop:after method="afterLog" pointcut-ref="myPointCut"></aop:after>
     <aop:after-returning method="afterReturningLog" pointcut-ref="myPointCut"
returning="result"></aop:after-returning>
    <aop:after-throwing method="afterThrowingLog" pointcut-ref="myPointCut"
throwing="ex"></aop:after-throwing>
    </aop:aspect>
<aop:aspect ref="transactionAspect" order="8">
    <aop:around method="around" pointcut-ref="myPointCut"></aop:around>
</aop:aspect>
</aop:config>
 
 
 4.事务
  4.1 JdbcTemplate
    Spring 的 JdbcTemplate 实际上就是 Spring 框架对原生 JDBC 的简单封装,类似于Javaweb 部分学过 DbUtils 工具类,主要是对数据表的数据进行增删改查操作。
    具体使用:
     第一步:创建动态Web工程,导入jar包
     
      第二步:在spring的配置文件配置JdbcTemplate
<!-- 1.加载properties文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 2.配置数据源信息 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
    <property name="username" value="${jdbc.userName}"/>
    <property name="password" value="${jdbc.password}"/>
    <property  name="driverClassName" value="${jdbc.driverClass}"/>
    <property  name="url" value="${jdbc.jdbcUrl}"/>
</bean>
 
<bean class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
</bean>
 
     第三步:在dao层使用JdbcTemplate
@Repository
public class EmployeeDao{
    @Autowired
    private JdbcTemplate jdbcTemplate;
    /************************************************************
        JdbcTemplate的使用:
        增删改操作:update()方法
    ************************************************************/
    public void insert(){
        String sql="insert into employee values(null,?,?)";
        jdbcTemplate.update(sql,"董阳",13000);
    }
}
 
 
JdbcTemplate的使用:
    增删改操作:update()方法
    批量增删改:batchUpdate()方法
    查询操作:
        1.查询一个pojo对象:queryForObject()
        2.查询一个单值:queryForObject()
        3.查询一个对象列表:query()方法
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值