Spring3(代理模式 Spring1案例补充 Aop 面试题)

一、代理模式

        在代理模式(Proxy Pattern)中,一个类代表另一个类的功能,这种类型的设计模式属于结构型模式。

        代理模式通过引入一个代理对象来控制对原对象的访问。代理对象在客户端和目标对象之间充当中介,负责将客户端的请求转发给目标对象,同时可以在转发请求前后进行额外的处理。

        在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

介绍

意图

为其他对象提供一种代理以控制对这个对象的访问。

主要解决的问题

  • 代理模式解决的是在直接访问某些对象时可能遇到的问题,例如对象创建成本高、需要安全控制或远程访问等。

使用场景

  • 当需要在访问一个对象时进行一些控制或额外处理时。

实现方式

  • 增加中间层:创建一个代理类,作为真实对象的中间层。
  • 代理与真实对象组合:代理类持有真实对象的引用,并在访问时进行控制。

关键代码

  • 代理类:实现与真实对象相同的接口,并添加额外的控制逻辑。
  • 真实对象:实际执行任务的对象。

应用实例

  • 快捷方式:Windows系统中的快捷方式作为文件或程序的代理。
  • 角色扮演:孙悟空作为高翠兰的代理,猪八戒无法区分。
  • 代售点:购买火车票时,代售点作为火车站的代理。
  • 支票:作为银行账户资金的代理,控制资金的访问。
  • Spring AOP:使用代理模式来实现面向切面编程。

优点

  • 职责分离:代理模式将访问控制与业务逻辑分离。
  • 扩展性:可以灵活地添加额外的功能或控制。
  • 智能化:可以智能地处理访问请求,如延迟加载、缓存等。

缺点

  • 性能开销:增加了代理层可能会影响请求的处理速度。
  • 实现复杂性:某些类型的代理模式实现起来可能较为复杂。

使用建议

  • 根据具体需求选择合适的代理类型,如远程代理、虚拟代理、保护代理等。
  • 确保代理类与真实对象接口一致,以便客户端透明地使用代理。

注意事项

  • 与适配器模式的区别:适配器模式改变接口,而代理模式不改变接口。
  • 与装饰器模式的区别:装饰器模式用于增强功能,代理模式用于控制访问。

结构

主要涉及到以下几个核心角色:

  • 抽象主题(Subject):

    • 定义了真实主题和代理主题的共同接口,这样在任何使用真实主题的地方都可以使用代理主题。
  • 真实主题(Real Subject):

    • 实现了抽象主题接口,是代理对象所代表的真实对象。客户端直接访问真实主题,但在某些情况下,可以通过代理主题来间接访问。
  • 代理(Proxy):

    • 实现了抽象主题接口,并持有对真实主题的引用。代理主题通常在真实主题的基础上提供一些额外的功能,例如延迟加载、权限控制、日志记录等。
  • 客户端(Client):

    • 使用抽象主题接口来操作真实主题或代理主题,不需要知道具体是哪一个实现类。

什么是代理模式?

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
通俗的来讲代理模式就是我们生活中常见的中介。
举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了

为什么要用代理模式?

中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,们还可以通过给代理
类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。 

有哪几种代理模式?

 我们有多种不同的方式来实现代理。如果按照代理创建的时期来进行分类的话
可以分为两种:
静态代理:
        静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。
动态代理:
动态代理是在程序运行时通过反射机制动态创建的。
动态代理分为:

  • 基于接口的动态代理(jdk自带)
  • 基于子类的动态代理(第三方) 

1. 静态代理

实现

 创建一个接口。

public interface IWomen {public void  makeEyesWithMan();//抛媚眼public void  happyWithMan();//开心
}

创建实现接口的实体类

public class PanJinLianImp implements IWomen{public void makeEyesWithMan() {System.out.println("=======回眸一笑,抛个媚眼======");}public void happyWithMan() {System.out.println("=========嘿嘿嘿,老娘来了~~~==========");}
}

代理

//代理 中间商
public class WangPoImp implements IWomen{//被代理IWomen iWomen;public WangPoImp() {}public WangPoImp(IWomen iWomen) {this.iWomen = iWomen;}public void makeEyesWithMan() {System.out.println("=======斟一壶酒,搞搞气氛======");iWomen.makeEyesWithMan();}public void happyWithMan() {iWomen.happyWithMan();}
}

请求

public class XiMenQing {public static void main(String[] args) {//1.目标(被代理对象)PanJinLianImp panJinLianImp = new PanJinLianImp();//2.代理WangPoImp wangPoImp = new WangPoImp(panJinLianImp);wangPoImp.makeEyesWithMan();wangPoImp.happyWithMan();}
}

2. 基于接口的动态代理(jdk自带)

基于接口的动态代理:

  • 特点:字节码随用随创建,随用随加载
  • 作用:不修改源码的基础上对方法增强

涉及的类:Proxy
提供者:JDK官方
如何创建代理对象:
          使用Proxy类中的newProxyInstance方法
创建代理对象的要求:
         被代理类最少实现一个接口,如果没有则不能使用
newProxyInstance方法的参数:

  • ClassLoader:类加载器
  •          它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
  •  Class[]:字节码数组
  •          它是用于让代理对象和被代理对象有相同方法。固定写法。
  • InvocationHandler:用于提供增强的代码

实现

接口

public interface ISinger {public void sing();public void rap();
}

实现类

public class CaiXuKunImp implements ISinger{public void sing() {System.out.println("========鸡你太美========");}public void rap() {System.out.println("=======rap=========");}
}

 测试

public class Test01 {public static void main(String[] args) {//1.被代理对象final CaiXuKunImp caiXuKunImp = new CaiXuKunImp();/*** 基于接口的动态代理:*  特点:字节码随用随创建,随用随加载*  作用:不修改源码的基础上对方法增强*  涉及的类:Proxy*  提供者:JDK官方*  如何创建代理对象:*      使用Proxy类中的newProxyInstance方法*  创建代理对象的要求:*      被代理类最少实现一个接口,如果没有则不能使用*  newProxyInstance方法的参数:*      ClassLoader:类加载器*          它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。*      Class[]:字节码数组*          它是用于让代理对象和被代理对象有相同方法。固定写法。*      InvocationHandler:用于提供增强的代码**/ISinger jinjinren = (ISinger) Proxy.newProxyInstance(caiXuKunImp.getClass().getClassLoader(), caiXuKunImp.getClass().getInterfaces(), new InvocationHandler() {/****  Object proxy=====》被代理对象的引用===(蔡徐坤对象)*  Method method====》执行的方法========(蔡徐坤唱歌方法)*  Object[] args====》执行方法的参数=====(蔡徐坤唱歌方法的参数)*  Object===========》执行方法的返回值===(蔡徐坤唱歌方法的返回值)* *//*** 作用:执行被代理对象的任何接口方法都会经过该方法* 方法参数的含义*  proxy   代理对象的引用*  method  当前执行的方法*  args    当前执行方法所需的参数*  Object  和被代理对象方法有相同的返回值*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("跳一段舞");Object obj = method.invoke(caiXuKunImp, args);//代表调用被代理对象的核心方法System.out.println("打一个篮球");return obj;}});//3.唱歌jinjinren.sing();System.out.println("***********************");jinjinren.rap();}
}

 

3. 基于子类的动态代理 

基于子类的动态代理

  • 涉及的类:Enhancer
  • 提供者:第三方cglib库
  • 开发环境:添加cglib依赖坐标

如何创建代理对象:
        使用Enhancer类中的create方法
创建代理对象的要求:
        被代理类不能是最终类
create方法的参数:

  • Class:字节码
    • 它是用于指定被代理对象的字节码。
  •  Callback:用于提供增强的代码
    •   它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。

此接口的实现类都是谁用谁写。
我们一般写的都是该接口的子接口实现类:MethodInterceptor

实现 

注入

   <dependencies><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.1_3</version></dependency></dependencies>

接口

public interface ISinger {public void sing();
}
public class ZhouShenImp implements ISinger{@Overridepublic void sing() {System.out.println("========大鱼海棠========");}
}
public class Test01 {public static void main(String[] args) {1.被代理对象final ISinger zhouShenImp = new ZhouShenImp();ISinger jingjiren = (ISinger) Enhancer.create(zhouShenImp.getClass(), new InvocationHandler() {/*** 2.创建代理对象* 参数1:被代理对象的字节码* 参数2:InvocationHandler* *//*** 执行被代理对象的任何方法都会经过该方法* @param proxy* @param method* @param args*    以上三个参数和基于接口的动态代理中invoke方法的参数是一样的* @param methodProxy :当前执行方法的代理对象* @return* @throws Throwable*/@Overridepublic Object invoke(Object o, Method method, Object[] objects) throws Throwable {System.out.println("====找一下蔡徐坤=====");Object obj =  method.invoke(zhouShenImp,objects);System.out.println("======和蔡徐坤一起基太美=====");return obj;}});//3.唱歌jingjiren.sing();}
}

二、Spring1案例补充

1. 常规实现

在Spring1中的基础上 实现 转钱功能

xml实现

项目状态:成功提交,失败不能做到一个业务方法一起回滚
分析原因:项目存在事务自动管理,且自动管理在dao层实现
解决方案:事务管理未来一定是在service层实现
方案思考:
    1.dao层不在进行事务管理,自动事务提交关闭
    2.业务类的每个业务方法中的多个dao操作,公用同一个连接connection对象
    3.ThreadLocal

dao层不变

service

接口新增

public void transfer(String sourceName,String targetName,int money);

实现类 重写该方法

@Overridepublic void transfer(String sourceName, String targetName, int money) {try {//开启事务transactionUtil.beginTx();Account sourceAccount = dao.findByName(sourceName);Account targetAccount = dao.findByName(targetName);sourceAccount.setAmoney(sourceAccount.getAmoney()-money);targetAccount.setAmoney(targetAccount.getAmoney()+money);dao.updateById(sourceAccount);int a =10/0;//模拟异常dao.updateById(targetAccount);//提交事务transactionUtil.commitTx();} catch (Exception e) {//回滚事务transactionUtil.rollbackTx();e.printStackTrace();} finally {//关闭事务transactionUtil.closeTx();}}

controller同理

 public void transfer(String sourceName,String targetName,int money);@Overridepublic void transfer(String sourceName, String targetName, int money) {service.transfer(sourceName,targetName,money);}

util        

ConnectionUtil
public class ConnectionUtil {//线程区域对象ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<Connection>();//数据源DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}//获取连接public Connection createConn() {try {//在口袋里面找连接Connection connection = connectionThreadLocal.get();//判断if (connection==null){connection=dataSource.getConnection();connectionThreadLocal.set(connection);}return  connection;} catch (SQLException throwables) {throwables.printStackTrace();}return  null;}//关闭连接public void closeConn(){connectionThreadLocal.remove();//解绑}
}

TransactionUtil

public class TransactionUtil {//专配连接工具类ConnectionUtil connectionUtil;public void setConnectionUtil(ConnectionUtil connectionUtil) {this.connectionUtil = connectionUtil;}//开启public void beginTx() {try {connectionUtil.createConn().setAutoCommit(false);} catch (SQLException throwables) {throwables.printStackTrace();}}//提交public void commitTx() {try {connectionUtil.createConn().commit();} catch (SQLException throwables) {throwables.printStackTrace();}}//回滚public void rollbackTx() {try {connectionUtil.createConn().rollback();} catch (SQLException throwables) {throwables.printStackTrace();}}//关闭public void closeTx() {try {connectionUtil.createConn().close();connectionUtil.closeConn();} catch (SQLException throwables) {throwables.printStackTrace();}}
}

同时需要在service 实现类装配事务工具类

//装配事务工具类TransactionUtil transactionUtil;public void setTransactionUtil(TransactionUtil transactionUtil) {this.transactionUtil = transactionUtil;}

dao层装配连接工具类

 //装配连接工具类ConnectionUtil connectionUtil;public void setConnectionUtil(ConnectionUtil connectionUtil) {this.connectionUtil = connectionUtil;}

在xml文件中注入

<!-- 注入连接工具类 --><bean id="connectionUtil" class="com.zkt.util.ConnectionUtil"><property name="dataSource" ref="dataSource"/></bean><!-- 注入事务工具类 --><bean id="transactionUtil" class="com.zkt.util.TransactionUtil"><property name="connectionUtil" ref="connectionUtil"/></bean><!-- 注入dao --><bean id="dao" class="com.zkt.dao.AccountDaoImp"><property name="queryRunner" ref="queryRunner"/><property name="connectionUtil" ref="connectionUtil"/></bean><!-- 注入service --><bean id="service" class="com.zkt.service.AccountServiceImp"><property name="dao" ref="dao"/><property name="transactionUtil" ref="transactionUtil"/></bean>

 2. 代理实现

在util下创建代理

public class ProxyFactory {//被代理对象装配IAccountService toServiceProxy;public void setToServiceProxy(IAccountService toServiceProxy) {this.toServiceProxy = toServiceProxy;}//装配事务TransactionUtil transactionUtil;public void setTransactionUtil(TransactionUtil transactionUtil) {this.transactionUtil = transactionUtil;}//创建代理public IAccountService createProxy(){IAccountService service = (IAccountService) Proxy.newProxyInstance(toServiceProxy.getClass().getClassLoader(), toServiceProxy.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object obj = null;try {transactionUtil.beginTx();obj = method.invoke(toServiceProxy, args);transactionUtil.commitTx();} catch (Exception e) {e.printStackTrace();transactionUtil.rollbackTx();} finally {transactionUtil.closeTx();}return obj;}});return  service;}
}

事务工具类不变

连接工具类

public class ConnectionUtil {//线程区域对象ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<Connection>();//数据源DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}//获取连接public Connection createConn() {try {//在口袋里面找连接Connection connection = connectionThreadLocal.get();//判断if (connection==null){connection=dataSource.getConnection();connectionThreadLocal.set(connection);}return  connection;} catch (SQLException throwables) {throwables.printStackTrace();}return  null;}//关闭连接public void closeConn(){connectionThreadLocal.remove();//解绑}
}

故service的transfer 正常写就行

 @Overridepublic void transfer(String sourceName, String targetName, int money) {Account sourceAccount = dao.findByName(sourceName);Account targetAccount = dao.findByName(targetName);sourceAccount.setAmoney(sourceAccount.getAmoney() - money);targetAccount.setAmoney(targetAccount.getAmoney() + money);dao.updateById(sourceAccount);
//        int a = 10 / 0;//模拟异常dao.updateById(targetAccount);}

最后 在xml文件中 注入就行

  <!-- 注入service(被代理对象) --><bean id="service" class="com.zkt.service.AccountServiceImp"><property name="dao" ref="dao"/></bean><!-- 注入service(代理对象) --><bean id="proxyService" class="com.zkt.service.AccountServiceImp" factory-bean="factory" factory-method="createProxy"/><bean id="factory" class="com.zkt.util.ProxyFactory"><property name="transactionUtil" ref="transactionUtil"/><property name="toServiceProxy" ref="service"/></bean><!-- 注入controller(消费者) --><bean id="controller" class="com.zkt.controller.AccountControllerImp"><property name="service" ref="proxyService"/></bean>

其余不变 即可实现

三、AOP

        AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术

        AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,
是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得
业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP 的作用及其优势

  •  作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
  •  优势:减少重复代码,提高开发效率,并且便于维护

AOP 的应用

  • 1.日志
  • 2.事务
  • 3.权限


Spring AOP 基于动态代理实现:

  • 如果被代理的对象,已经实现某个接口,则 Spring AOP 会使用 JDK Proxy(反射),基于接口的方式,创建代理对象(JDK动态代理的核心是InvocationHandler接口和Proxy类);
  • 如果被代理的对象,没有实现某个接口,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP 会使用 Cglib,基于继承的方式,生成一个被代理对象的子类来作为代理(Cglib动态代理的核心是MethodInterceptor接口和Enhancer类);
     

AOP术语:

AOP通知类型
        AOP将抽取出来的共性功能称为通知;通知类型:以通知在上下文中的具体位置作为划分

  •         前置通知(Before)
  •         后置通知(After)
  •         返回通知(After-returning)
  •         异常通知(After-throwing)
  •         环绕通知(Around)

AOP连接点(Join point)
        AOP将所有的方法都视为连接点,不管是接口里面的抽象方法,还是实现类里面的重写方法,都是连接点

AOP切点(Pointcut)
        AOP将可能被抽取共性功能的方法称为切入点。切入点是连接点的子集

AOP目标对象(Target):

        就是挖掉功能的方法对应的类生的对象,这种对象是无法直接完成最终工作的

AOP织入(Weaving):

        就是将挖掉的功能回填的动态过程

AOP切面:

        切点+通知


实现步骤:

  • 1.添加依赖,aop与aspectj表达式的依赖
  • 2.创建spring的主配置文件,bean内的命名空间要添加aop的
  • 3.创建业务代码并编写日志记录代码(事务管理代码)
  • 4.将业务层与日志记录层注入spring容器
  • 5.<aop:config>--aop配置
  •         aop:aspect--aop切面
  •                aop:before--通知内容与通知类型

切点表达式配置语法:
 

execution(修饰符 返回值 包名称.类名称.方法名称(参数列表))

eg:
    execution(public void com.apesource.service.ServiceImp.findAll())
1.修饰符可以省略代表任意
    execution(返回值 包名称.类名称.方法名称(参数列表))
2.返回值可以使用“*”代表任意
    execution(* 包名称.类名称.方法名称(参数列表))
3.包名可以使用“*”代表任意名称
    execution(* *.*.*.类名称.方法名称(参数列表))
4.包名可以使用“..”代表任意个数
    execution(* *...类名称.方法名称(参数列表))
5.类名与方法名可以使用“*”代表任意
    execution(* *...*.*(参数列表))
6.参数列表可以使用".."代表任意个数任意类型
    execution(* *...*.*(..))
    如果有参数
        int======>int
        String===>java.lang.String

xml实现日志记录

poi.xml 导入坐标

AOP 不需要到导 因为context包含了

<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.13</version></dependency>

service

public interface IAccountService {public void save(int i);public void update();public int delete();
}public class AccountServiceImp implements IAccountService {public void save(int i) {System.out.println("业务层的新增方法:" + i);}public void update() {System.out.println("业务层的修改方法");
//        int a = 10/0;}public int delete() {System.out.println("业务层的删除方法");return 0;}
}

util

public class Logger {public void beforeMethod() {System.out.println("日志类logger===前置通知");}public void returnMethod() {System.out.println("日志类logger===返回通知");}public void throwMethod() {System.out.println("日志类logger===异常通知");}public void afterMethod() {System.out.println("日志类logger===后置通知");}
}

applicationContext.xml

 <!-- 注入业务层 --><bean id="accountServiceImp" class="com.zkt.service.AccountServiceImp"/><!-- 注入日志记录层(通知) --><bean id="logger" class="com.zkt.util.Logger"/><!-- 配置AOP --><aop:config><!-- 配置切面  --><aop:aspect id="aopAspect" ref="logger"><!-- 切点 --><aop:pointcut id="dian" expression="execution(* com.zkt.service.AccountServiceImp.* (..))"/><!-- 通知 --><aop:before method="beforeMethod" pointcut-ref="dian"/><aop:after-returning method="returnMethod" pointcut-ref="dian"/><aop:after-throwing method="throwMethod" pointcut-ref="dian"/><aop:after method="afterMethod" pointcut-ref="dian"/></aop:aspect></aop:config>

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Test01 {@Autowiredpublic IAccountService service;@Testpublic void show1(){service.update();
//        System.out.println("===================");
//        service.save(1);
//        System.out.println("===================");
//        service.delete();}
}

注解实现日志记录

导入坐标同理

加入注解

util使用环绕通知实现

@Component
@Aspect//切面
public class Logger {@Pointcut("execution(* com.zkt.service.AccountServiceImp.* (..))")public void dian() {}
//    @Before("dian()")
//    public void beforeMethod() {
//        System.out.println("日志类logger===前置通知");
//    }
//
//    @AfterReturning("dian()")
//    public void returnMethod() {
//        System.out.println("日志类logger===返回通知");
//    }
//
//    @AfterThrowing("dian()")
//    public void throwMethod() {
//        System.out.println("日志类logger===异常通知");
//    }
//
//    @After("dian()")
//    public void afterMethod() {
//        System.out.println("日志类logger===后置通知");
//    }//环绕通知@Around("dian()")public Object AroundLogger(ProceedingJoinPoint pjp) {Object returnobj = null;//保存主业务方法的返回值try {//1.前置通知System.out.println("环绕通知===》前置通知");Object[] objs = pjp.getArgs();//主业务方法的参数returnobj = pjp.proceed(objs);//调用主业务方法//3.后置通知System.out.println("环绕通知===》返回通知");} catch (Throwable tw) {//4.异常通知System.out.println("环绕通知===》异常通知");} finally {//5.最终通知System.out.println("环绕通知===》后置通知");}return returnobj;}
}

测试 异常情况

正常情况

四、面试题

16. Spring框架中的Bean生命周期

  • spring bean的生命周期总体分四个阶段:实例化=>属性注入 =>初始化=>销毁
  • step1 实例化bean根据配置文件中 bean 的定义,利用 java reflection 反射技术创建 bean的实例
  • step2 注入对象依赖的属性值(或对象)
  • step3 处理各种Aware接口:spring 会检测该 bean 是否实现了 xxxaware 接口,通过Aware类型的接口,可以让spring框架为当前 bean 注入相应的内容
    • 如果 bean 实现 beannameaware 接口,会调用它实现的 setbeanname(string beanid)方 法,注入 bean 的名字                
    • 如果 bean 实现 beanclassloaderaware 接口,调用 setbeanclassloader()方法,注入classloader 对象的实例
    • 如果 bean 实beanfactoryaware 接口,会调用它实现的 setbeanfactory()方法,注入的是 spring工厂。如果 bean 实现 applicationcontextaware 接口,会调用 setapplicationcontext()方法,注入 spring 上下文
  • step4 执行BeanPostProcessor前置处理:如果想对 bean 进行一些自定义的前置处理,那么可以让 bean 实现了 beanpostprocessor 接口,将会在该阶段调用 postprocessbeforeinitialization(object obj,string s)
  • Step5 执行initializingbean初始化方法:如果 bean 实现了 InitializingBean接口,执行afeterPropertiesSet() 方法
  • step6 执行init-method自定义初始化方法:如果 bean 在 spring 配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法
  • step7 执行beanpostprocessor后置处理:如果这个 bean 实现了beanpostprocessor接口, 用 postprocessAfterInitialization(object ob,string s)方法,由于这个方法是在 bean初始化结束后调用

以上几个步骤完成后, bean 已经被正确创建,可以正常使用这个bean了

  • step8 执行disposablebean销毁bean:当 bean 不再需要时,会经过清理阶段,如果 bean 实现了 disposablebean 这个接口,会调用其实现的 destroy()方法执行销毁
  • step9 执行destroy-method自定义初始化方法如果 bean 在 spring 配置文件中配置了 destroy-method 属性,则会自动调用其配置的初始化方法

17. spring 框架如何解决循环依赖?

循环依赖问题是指∶ 类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。

Spring使用三级缓存来解决循环依赖

18. Spring 框架中有哪些注解?

用于声明 bean 的注解:

  • @Component:定义通用bean的注解,可标注任意类为 bean。如果一个 bean 不知道属于 哪个层,可以使用 @component 注解标注
  • @Repository:定义数据访问层bean的注解
  • @Service:定义业务层bean的注解
  • @Controller:定义控制器bean的注解

用于注入的注解:

  • @Autowired:按类型自动注入
  • @Qualifier:按名称自动注入

声明配置、扫描、启用特性的注解:

  • @Configuration:声明配置类
  • @ComponentScan:组件扫描
  • @EnablesScheduling:启用任务调度
  • @EnableAspectJAutoProxy:启用自动代理工厂

19. Spring 框架中用到的设计模式

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/380706.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Visual Studio 2022美化

说明&#xff1a; VS版本&#xff1a;Visual Studio Community 2022 背景美化 【扩展】【管理扩展】搜索“ClaudiaIDE”&#xff0c;【下载】&#xff0c;安装完扩展要重启VS 在wallhaven下载壁纸图片作为文本编辑器区域背景图片 【工具】【选项】搜索ClaudiaIDE&#xff…

基于python深度学习遥感影像地物分类与目标识别、分割实践技术应用

目录 专题一、深度学习发展与机器学习 专题二、深度卷积网络基本原理 专题三、TensorFlow与Keras介绍与入门 专题四、PyTorch介绍与入门 专题五、卷积神经网络实践与遥感图像场景分类 专题六、深度学习与遥感图像检测 专题七、遥感图像检测案例 专题八、深度学习与遥感…

四、GD32 MCU 常见外设介绍

系统架构 1.RCU 时钟介绍 众所周知&#xff0c;时钟是MCU能正常运行的基本条件&#xff0c;就好比心跳或脉搏&#xff0c;为所有的工作单元提供时间 基数。时钟控制单元提供了一系列频率的时钟功能&#xff0c;包括多个内部RC振荡器时钟(IRC)、一个外部 高速晶体振荡器时钟(H…

《背包乱斗》为什么好玩 苹果电脑怎么玩《背包乱斗》游戏 mac怎么玩steam windows游戏

在当今竞争激烈的游戏市场中&#xff0c;《背包乱斗》以其独特的魅力在众多作品中脱颖而出&#xff0c;吸引了大量玩家的关注和喜爱。其创新的游戏机制和不断迭代的内容&#xff0c;加之出色的视觉效果和社区建设&#xff0c;使其成为了游戏界的一股清流。 一、《背包乱斗》为…

【计算机视觉】siamfc论文复现实现目标追踪

什么是目标跟踪 使用视频序列第一帧的图像(包括bounding box的位置)&#xff0c;来找出目标出现在后序帧位置的一种方法。 什么是孪生网络结构 孪生网络结构其思想是将一个训练样本(已知类别)和一个测试样本(未知类别)输入到两个CNN(这两个CNN往往是权值共享的)中&#xff0…

jmeter部署

一、windows环境下部署 1、安装jdk并配置jdk的环境变量 (1) 安装jdk jdk下载完成后双击安装包&#xff1a;无限点击"下一步"直到完成&#xff0c;默认路径即可。 (2) jdk安装完成后配置jdk的环境变量 找到环境变量中的系统变量&#xff1a;此电脑 --> 右键属性 …

Figma 中文版指南:获取和安装汉化插件

Figma是一种主流的在线团队合作设计工具&#xff0c;也是一种基于 Web 端的设计工具。在当今的设计时代&#xff0c;Figma 的使用满足了每个人的设计需求&#xff0c;不仅可以实现在线编辑&#xff0c;还可以方便日常管理&#xff0c;有效提高工作效率。然而&#xff0c;相信很…

RPM、YUM 安装 xtrabackup 8 (mysql 热备系列一)包含rpm安装 mysql 8 配置主从

RPM安装 percona-xtrabackup-80-8.0.35-30.1.el7.x86_64.rpm 官网&#xff1a; https://www.percona.com/ 下载地址&#xff1a; https://www.percona.com/downloads wget https://downloads.percona.com/downloads/percona-distribution-mysql-ps/percona-distribution-mysq…

【Vue3】响应式数据

【Vue3】响应式数据 背景简介开发环境基本数据类型对象数据类型使用 reactive 定义对象类型响应式数据使用 ref 定义对象类型响应式数据 ref 和 reactive 的对比使用原则建议 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0…

Linux系统之部署扫雷小游戏(三)

Linux系统之部署扫雷小游戏(三) 一、小游戏介绍1.1 小游戏简介1.2 项目预览二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍三、检查本地环境3.1 检查系统版本3.2 检查系统内核版本3.3 检查软件源四、安装Apache24.1 安装Apache2软件4.2 启动apache2服务4.3 查看apache2服…

项目管理进阶之RACI矩阵

前言 项目管理进阶系列续新篇。 RACI&#xff1f;这个是什么矩阵&#xff0c;有什么用途&#xff1f; 在项目管理过程中&#xff0c;如Team规模超5以上时&#xff0c;则有必要采用科学的管理方式&#xff0c;满足工作需要。否则可能事倍功半。 Q&#xff1a;什么是RACI矩阵 …

【HarmonyOS】HarmonyOS NEXT学习日记:五、交互与状态管理

【HarmonyOS】HarmonyOS NEXT学习日记&#xff1a;五、交互与状态管理 在之前我们已经学习了页面布局相关的知识&#xff0c;绘制静态页面已经问题不大。那么今天来学习一下如何让页面动起来、并且结合所学完成一个代码实例。 交互 如果是为移动端开发应用&#xff0c;那么交…

Unity Apple Vision Pro 开发(四):体积相机 Volume Camera

文章目录 &#x1f4d5;教程说明&#x1f4d5;教程内容概括&#x1f4d5;体积相机作用&#x1f4d5;创建体积相机&#x1f4d5;添加体积相机配置文件&#x1f4d5;体积相机配置文件参数&#x1f4d5;体积相机的边界盒大小&#x1f4d5;体积相机边界盒大小和应用边界盒大小的区别…

弹性网络回归(Elastic Net Regression)

弹性网络回归&#xff08;Elastic Net Regression&#xff09;的详细理论知识推导 理论背景 弹性网络回归结合了岭回归&#xff08;Ridge Regression&#xff09;和Lasso回归&#xff08;Lasso Regression&#xff09;的优点&#xff0c;通过引入两个正则化参数来实现特征选择…

kubernetes k8s Deployment 控制器配置管理 k8s 红蓝部署 金丝雀发布

目录 1、Deployment控制器&#xff1a;概念、原理解读 1.1 Deployment概述 1.2 Deployment工作原理&#xff1a;如何管理rs和Pod&#xff1f; 2、Deployment资源清单文件编写技巧 3、Deployment使用案例&#xff1a;创建一个web站点 4、Deployment管理pod&#xff1a;扩…

通义千问AI模型对接飞书机器人-模型配置(2-1)

一 背景 根据业务或者使用场景搭建自定义的智能ai模型机器人&#xff0c;可以较少我们人工回答的沟通成本&#xff0c;而且可以更加便捷的了解业务需求给出大家设定的业务范围的回答&#xff0c;目前基于阿里云的通义千问模型研究。 二 模型研究 参考阿里云帮助文档&#xf…

持续集成04--Jenkins结合Gitee创建项目

前言 在持续集成/持续部署&#xff08;CI/CD&#xff09;的旅途中&#xff0c;Jenkins与版本控制系统的紧密集成是不可或缺的一环。本篇“持续集成03--Jenkins结合Gitee创建项目”将引导如何将Jenkins与Gitee&#xff08;一个流行的Git代码托管平台&#xff09;相结合&#xff…

修改了mybatis的xml中的sql不重启服务器如何动态加载更新

目录 一、背景 二、注意 三、代码 四、使用示例 五、其他参考博客 一、背景 开发一个报表功能&#xff0c;好几百行sql&#xff0c;每次修改完想自测下都要重启服务器&#xff0c;启动一次服务器就要3分钟&#xff0c;重启10次就要半小时&#xff0c;耗不起时间呀。于是在…

【Android】Activity的生命周期

Activity的生命周期 1.返回栈 其实Android是使用任务&#xff08;task&#xff09;来管理Activity的&#xff0c;一个任务就是一组存放在栈里的Activity的集合&#xff0c;这个栈也被称作返回栈&#xff08;back stack&#xff09;。栈是一种后进先出的数据结构&#xff0c;在…

自动驾驶-预测概览

通过生成一条路径来预测一个物体的行为&#xff0c;在每一个时间段内&#xff0c;为每一辆汽车重新计算预测他们新生成的路径&#xff0c;这些预测路径为规划阶段做出决策提供了必要信息 预测路径有实时性的要求&#xff0c;预测模块能够学习新的行为。我们可以使用多源的数据…