依赖注入&动态代理&AOP

在这里插入图片描述

一 简述Spring核心

​ 控制反转:IOC—>创建对象的过程

​ 依赖注入: DI---->初始化对象属性

面向切面:AOP--->不改变原码的前提,在其方法前后插入业务逻辑代码

二 依赖注入

2.1 set方法注入

简述: B类对象持有A类对象,spring通过无参构造反射创建B对象,并将A对象当作参数传入set方法赋值给A对象变量;

set方法注入用的较多,它可以执行无参构造方法创建对象,比较灵活!

spring.xml

<bean id="a" class="cn.xgl.set.A"></bean>
<bean id="b" class="cn.xgl.set.B">
    <property name="a" ref="a"/>
</bean>

java

public class A {

}
/***********************************************/
public class B {
    private A a;

    public B() {
        System.out.println("执行了B无参构造...");
    }

    public A getA() {
        return A;
    }
    //配置bean标签,spring通过参数传递对象并赋值--->set依赖注入
    public void setA(A a) {
        System.out.println("执行了set方法....");
        this.a = a;
    }
}

2.2 构造方法注入

2.2.1 构造方法参数名称注入 name
<bean id="a" class="cn.xgl.construct.A"></bean>
<bean id="b" class="cn.xgl.construct.B">
    <constructor-arg name="a" ref="a"/>
    <constructor-arg name="name" value="小猪猪"/>
    <constructor-arg name="phone" value="12373468376"/>
</bean>
2.2.2 构造方法参数顺序注入 index
<<bean id="a" class="cn.xgl.construct.A"></bean>
<bean id="b" class="cn.xgl.construct.B">
    <constructor-arg index="0" ref="a"/>
    <constructor-arg index="1" value="小猪猪"/>
    <constructor-arg index="2" value="12373468376"/>
</bean>
2.2.3 构造方法参数类型注入 type 相同类型需要注意顺序
<!--如果类型一样,按constructor-arg顺序匹配-->
<bean id="a" class="cn.xgl.construct.A"></bean>
<bean id="b" class="cn.xgl.construct.B">
    <constructor-arg type="cn.xgl.construct.A" ref="a"/>
    <constructor-arg type="java.lang.String" value="小猪猪"/>
    <constructor-arg type="java.lang.String" value="******"/>
</bean>

3.内部bean与外部bean

如果有一个是我们自己的对象,可以使用 —>内部bean(局部)与外部bean(全局)

<!--外部部bean-->
<bean id="a" class="cn.xgl.construct.A"></bean>
<bean id="b" class="cn.xgl.construct.B">
    <property name="a" ref="a"/>
</bean>-->

<!--内部bean-->
<bean id="c" class="cn.xgl.construct.C">
    <property name="D">
        <bean class="cn.xgl.construct.D"></bean>
    </property>
</bean>-->

三 Set注入练习

public class Employee {
    // 简单属性
    private Long id;
    private String name;
    private Boolean sex;
    private BigDecimal salary;

    // 对象属性
    private String[] arrays;
    private List<String> list;
    private Set<String> set;
    private Map<String, Object> map;

    //其他bean
    private Set<OtherBean> otherBeanSet;
    private List<OtherBean> otherBeanList;
    private Properties props1;
    private Properties props2;
    
    //省略构造/get/set/tostring方法....
    //ps:
    //集合或数组使用它打印: System.out.println("--->"+ Arrays.asList(arrays));
}
<bean id="otherBean01"  class="cn.xgl.practice.OtherBean"/>
<bean id="otherBean02"  class="cn.xgl.practice.OtherBean"/>
<bean id="otherBean03"  class="cn.xgl.practice.OtherBean"/>
<bean class="cn.xgl.practice.Employee">
    <!--简单类型之属性注入 property标签_____Spring自动类型转换-->
    <property name="id" value="1"/>
    <property name="name" value="小猪猪"/>
    <property name="sex" value="true"/>
    <property name="salary" value="10000"/>


    <!--简单类型的数组类型注入 方式一使用array标签-->
    <!--<property name="arrays">
           <array>
               <value>1</value>
               <value>2</value>
               <value>3</value>
           </array>
       </property>-->
    <!--简单类型的数组类型注入 方式二,号分隔-->
    <property name="arrays" value="1,2,3,4"/>

    <!--简单类型的List集合类型注入-->
    <!--<property name="list">
           <list>
              <value>技术部</value>
              <value>iT部</value>
              <value>人事部</value>
           </list>
       </property>-->
    <property name="list" value="人事部,技术部"/>

    <!--简单类型的Set类型注入 方式二,号分隔-->
    <!--<property name="set" value="人事部,技术部"/>-->
    <property name="set">
        <set>
            <value>set1</value>
            <value>set2</value>
            <value>set3</value>
        </set>
    </property>

    <!--简单类型___map属性的注入-->
    <property name="map">
        <map>
            <entry key="map1" value="put1"/>
            <entry key="map2" value="put2"/>
            <entry key="map3" value="put3"/>
            <entry key="map3" value-ref="otherBean01"/>
        </map>
    </property>

    <!--bean类型的数组或集合注入 -->
    <property name="otherBeanList">
        <list>
            <!--外部bean-->
            <!--<ref bean="otherBean01"/>
                <ref bean="otherBean02"/>
                <ref bean="otherBean03"/>-->

            <!--内部bean  不去id也不会报错,作用域问题-->
            <bean id="otherBean01"  class="cn.xgl.practice.OtherBean"/>
            <bean id="otherBean02"  class="cn.xgl.practice.OtherBean"/>
            <bean id="otherBean03"  class="cn.xgl.practice.OtherBean"/>
        </list>
    </property>

    <property name="otherBeanSet">
        <list>
            <!--外部bean-->
            <!--<ref bean="otherBean01"/>
               <ref bean="otherBean02"/>
               <ref bean="otherBean03"/>-->

            <!--内部bean  不去id也不会报错,作用域问题-->
            <bean id="otherBean01"  class="cn.xgl.practice.OtherBean"/>
            <bean id="otherBean02"  class="cn.xgl.practice.OtherBean"/>
            <bean id="otherBean03"  class="cn.xgl.practice.OtherBean"/>
        </list>
    </property>

    <!--
            注入 Properties props1
            Properties是HashTable的子类,但是要求键和值都必须是String
            通常情况下,项目中经常用jdbc.properties 解决硬编码问题            
            每一个prop标签就表示一个键值对  
    -->
    <property name="props1">
        <!--支持中文,属性值无乱码-->
        <props>
            <prop key="driverClassName">com.mysql.jdbc.Driver</prop>
            <prop key="url">jdbc:mysql://localhost:3306/jpa</prop>
            <prop key="username">root</prop>
            <prop key="password">123456</prop>
            <prop key="name">张三丰</prop>
        </props>
    </property>

    <!--
            Properties props2
            还可以直接写一个value标签,然后将一个jdbc.properties文件的文本内容直接写在value标签内部
            这个写法不支持中文(不是注释内容,而是等号后面的值)
        -->
    <property name="props2">
        <value>
            ####注释
            driverClassName=com.mysql.jdbc.Driver
            url=jdbc:mysql://localhost:3306/jpa
            username=root
            password=123456
            name=小猪猪
        </value>
    </property>

</bean>

四 代理模式

简述: 是一种设计模式,其目的是为了降低耦合度,具有良好的扩展性;

在这里插入图片描述

4.1 静态代理

简述: B 和 C 分别实现A接口,其中B类对象中持有A对象作为属性,在B重写的方法中调用A类对象的方法,并在其方法前后插入业务逻辑代码;

缺陷: 被代理对象固定,代理业务固定

/**
 * 静态代理模式:
 *      抽象主体角色: 房源   接口
 *      真实主题角色: 房东   实现接口
 *      代理主题角色: 中介   实现接口
 *
 *      代理对象持有真实主体角色对象,在执行真实角色的方法前后可以插入业务逻辑
 *      便于扩展
 *      缺陷: 被代理对象不能动态改变,代理业务固定
 */
public interface Housing {
    void look();//看房子
    void sign();//签合同
    void rent();//租金
}
public class Intermediary implements Housing{
    private Housing housing;

    public Intermediary(Housing housing) {
        this.housing = housing;
    }

    @Override
    public void look() {
        System.out.println("看房前");
        housing.look();
        System.out.println("看房后");
    }

    @Override
    public void sign() {
        System.out.println("签合同前");
        housing.sign();
        System.out.println("签合同后");
    }

    @Override
    public void rent() {
        System.out.println("收租金前");
        housing.rent();
        System.out.println("收租金后");
    }

    @Override
    public String toString() {
        return "Intermediary{" +
                "housing=" + housing +
                '}';
    }
}
public class Landlord implements Housing {
    @Override
    public void look() {
        System.out.println("看房子");
    }

    @Override
    public void sign() {
        System.out.println("签合同");
    }

    @Override
    public void rent() {
        System.out.println("收租金");
    }
}

public class MyInvocationHandler implements InvocationHandler {
    //被代理对象
    private Housing landlord;

    public MyInvocationHandler(Housing landlor) {
        this.landlord = landlor;
    }

    /**
     * 调用代理对象的方式时自动执行
     * @param proxy
     * @param method  被代理对象的方法
     * @param args    参数
     * @return        Objcet
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res = method.invoke(landlord, args);
        System.out.println("res___"+res);
        return res;
    }
}
@Test
public void test() throws Exception {
    Housing landlord = new Landlord();
    Housing intermediary = new Intermediary(landlord);
    intermediary.look();
    intermediary.sign();
    intermediary.rent();
}
/*结果:
    看房前
    看房子
    看房后
    签合同前
    签合同
    签合同后
    收租金前
    收租金
    收租金后*/

4.2 动态代理

简述: 相较静态代理而言,不用手动创建代理类和代理对象,使用JDK的API自动创建代理对象

实体类与上的Housing 和 Landlord一致

public class Dynamic_ProxyTest {
    /**
     * ClassLoader loader,   类加载器
       Class<?>[] interfaces, 被代理对象实现过的所有接口,字节码数组
       InvocationHandler h    执行处理器,接口
     * @throws Exception
     */
    @Test
    public void test() throws Exception {
        //创建被代理对象
        Housing Landlord = new Landlord();
        //获取当前类的字节码加载器
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        //获取被代理对象实现的接口
        Class<?>[] interfaces = Landlord.getClass().getInterfaces();
        //将被代理对象作为参数传入,返回一个Objcet对象
        MyInvocationHandler handler = new MyInvocationHandler(Landlord);

        //JDK动态代理生成一个代理对象(代理类不用自己写其对象也不用创建)
        Housing proxy =(Housing)Proxy.newProxyInstance(loader, interfaces, handler);

        //执行代理对象方法时,其实就是在执行MyInvocationHandler中的invke方法
        //本质上是在执行被代理对象的方法
        proxy.look();
        System.out.println(proxy); //cn.xgl.static_proxy.Landlord@3581c5f3
        System.out.println(proxy.getClass()); //class com.sun.proxy.$Proxy4 ???
    }
}

动态代理的原理

/**
         * com.sun.proxy.$Proxy4  没有创建却打印了,为什么??
         *字符串拼接成一个java类的源代码字符串(持有一个被代理对象作为属性,实现被代理对象实现过的所有接口,重写			  接口的方法),保存在内存中
         *利用输出流将其输入至$Proxy4.java文件中
         *使用动态编译技术,获取字节码文件 .class
         *使用类加载器将字节码文件加载到JVM中运行,获取字节码对象
         *使用反射技术,获取代理对象,  字节码.newInstance()
         *返回该对象
         *
*/

五 SpringAop

5.1 xml配置

<!--扫描包-->
<context:component-scan base-package="cn.xgl.spring_aop_xmlconfig.service"/>

<!--模拟事务管理器_切面逻辑类-->
<bean id="tr" class="cn.xgl.spring_aop_xmlconfig.service.TransactionManager"/>

<!--开启自动代理-->
<aop:aspectj-autoproxy/>

<!--
            execution__表达式
                  第一个*____任意返回值类型
                  第二个*____任意类型
                  第三个*____任意方法
                  (..)______参数列表

           aop:pointcut_____切点_何地插入业务逻辑
      -->
<aop:config>
  <aop:pointcut id="pointcut" expression="execution(* cn.xgl.spring_aop_xmlconfig.service.*.*(..))"/>
    <aop:aspect ref="tr">
        <!-- 以下配置问题:
                 当有异常时_开启事务_ 执行xmlConfig_提交事务_回滚事务
                 发生异常时不能提交事务___解决使用环绕通知
         -->
        <!--前置通知 切点之前执行begin;
                  <aop:before method="begin" pointcut-ref="pointcut"/>
                     后置通知 切点之后执行commit;
                  <aop:after method="commit" pointcut-ref="pointcut"/>
                     异常通知 切点异常时执行rollback;
                  <aop:after-throwing method="rollback" pointcut-ref="pointcut"/>
                     最终通知 常用于关闭资源;
                  <aop:after-returning method="close" pointcut-ref="pointcut"/>-->
        <aop:around method="around" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>

/**
    * 模拟事务管理器
    *  切面逻辑类
    */
public class TransactionManager {
    public void begin() {
        System.out.println("开启事务");
    }
    public void commit() {
        System.out.println("提交事务");
    }
    public void rollback() {
        System.out.println("回滚事务");
    }
    public void close() {
        System.out.println("关闭资源");
    }

    /**
     * 环绕通知_自定义执行顺序
     *      发生异常时,回滚事务,关闭资源
     * @param joinPoint
     * @return
     */
    public Object around(ProceedingJoinPoint joinPoint){
        Object object = null;
        try {
            this.begin();
            joinPoint.proceed();
            this.commit();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            this.rollback();
        }finally {
            this.close();
        }
        return object;
    }
}

5.2 全注解

<!--扫描包-->
<context:component-scan base-package="cn.xgl.spring_aop_annotation"/>

<!--开启自动代理  全注解必须开启-->
<aop:aspectj-autoproxy/>
@Component   //将创建对象的权力交个spring
@Aspect      //当前类是一个切面
public class TransactionManager {
    /**
     * 定义一个切点,以方法名() 为id
     */
    @Pointcut("execution(* cn.xgl.spring_aop_annotation.service.*.*(..))")
    public void pointcut(){}

    //@Before("pointcut()")
    public void begin() {
        System.out.println("开启事务");
    }
    //@After("pointcut()")
    public void commit() {
        System.out.println("提交事务");
    }
    //@AfterThrowing("pointcut()")
    public void rollback() {
        System.out.println("回滚事务");
    }
    //@AfterReturning("pointcut()")
    public void close() {
        System.out.println("关闭资源");
    }

    /**
     * 环绕通知_自定义执行顺序
     *      发生异常时,回滚事务,关闭资源
     * @param joinPoint
     * @return
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint){
        Object object = null;
        try {
            this.begin();
            joinPoint.proceed();
            this.commit();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            this.rollback();
        }finally {
            this.close();
        }
        return object;
    }
}

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOP动态代理是利用JDK的反射机制,在程序执行的时候动态创建代理对象,并动态指定要代理的对象。它的作用是在不改变已有代码的基础上,通过代理来横向拓展增强其他的功能,减少重复的代码,专注业务逻辑代码,解耦业务代码与非业务代码的关系。\[1\] 在Spring AOP中,实现动态代理的方式有两种:静态代理和基于配置的动态代理。静态代理是通过手动编写代理来实现,而基于配置的动态代理是通过在配置文件中进行配置来建立代理对象与目标对象之间的联系。\[2\] 在配置文件中,可以使用ProxyFactoryBean来创建代理对象,并指定要代理的目标对象、拦截器和代理接口。通过这种方式,可以实现对目标对象的代理,并在代理对象中添加额外的功能。\[2\] 然而,在配置过程中可能会遇到一些问题,比如依赖注入失败的异常。这可能是因为没有找到符合要求的bean,或者注入的依赖项没有正确配置。需要检查配置文件和注解的使用,确保所有的依赖项都正确配置和注入。\[3\] #### 引用[.reference_title] - *1* [SpringAOP动态代理](https://blog.csdn.net/a55566999/article/details/125428712)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v4^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Spring AOP动态代理方式](https://blog.csdn.net/m0_53536589/article/details/123151359)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v4^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值