一、Spring

一、Spring

1. Spring如何解决循环依赖的问题?

详情参考: https://blog.csdn.net/qq_36381855/article/details/79752689
(即A依赖B,B依赖C、C依赖A)
循环依赖种类:
①构造器的循环依赖。【这个Spring解决不了】:A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象;
②【setter循环依赖】field属性的循环依赖:A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象;
为啥Spring不能解决“A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象(即构造器的循环依赖)”这类问题了!因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。

两种方式:?
第一步:将构造函数注入方式改为 属性注入方式 ;(因为Spring解决不了)
第二步:使用延迟加载(使用到的时候再创建);(三级缓存?参考http://zengbingo.com/p/1985.html

1. Spring中用到的设计模式有哪些?

Spring 中经典的 9 种设计模式:

单例模式、工厂模式?(简单工厂、工厂方法)、代理模式、适配器模式、包装器模式(装饰器模式)、观察者模式、模板方法模式、策略模式等;
①单例模式:scope值为 singleton、或者不写时默认单例 ;scope值改为 prototype表示多例(singleton=“true|false” 或者 scope=“singleton|prototype”来指定);
②简单工厂:

<bean id="singletonBean" class="com.itxxz.HelloItxxz">
   <constructor-arg>
      <value>Hello! 这是singletonBean!value>
   </constructor-arg>
</bean>

③工厂方法:

public class StaticFactoryBean {
  public static Integer createRandom() {
   	 return new Integer(new Random().nextInt());
   }
}
....
<bean id="random" class="example.chapter3.StaticFactoryBean" factory-method="createRandom" />

④代理模式:在aop中有体现,比如Jdk动态代理和Cglib子类代理;
⑤适配器模式:SpringMVC中的适配器HandlerAdatper;
⑥包装器模式:Spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator;
⑦观察者模式:Spring中Observer模式常用的地方是listener的实现。如ApplicationListener;
⑧模板方法模式:Spring中的JdbcTemplate;
⑨策略模式:;

详细参考一:
https://mp.weixin.qq.com/s/kwgV7Rhxv7wV7DOWmS9NzQ
参考二:
https://www.cnblogs.com/AndyAo/p/8666385.html


1. Spring中Bean生命周期?

谈谈你对Spring Bean生命周期的理解【面试】):https://blog.csdn.net/cool_summer_moon/article/details/106149339

详细参考: https://www.jianshu.com/p/1dec08d290c1
只有四个!
实例化 -> 属性赋值 -> 初始化 -> 销毁!
是的,Spring Bean的生命周期只有这四个阶段。把这四个阶段和每个阶段对应的扩展点糅合在一起虽然没有问题,但是这样非常凌乱,难以记忆。要彻底搞清楚Spring的生命周期,首先要把这四个阶段牢牢记住。实例化和属性赋值对应构造方法和setter方法的注入,初始化和销毁是用户能自定义扩展的两个阶段。在这四步之间穿插的各种扩展点,稍后会讲
1、实例化 Instantiation
2、属性赋值 Populate
3、初始化 Initialization
4、销毁 Destruction
实例化 -> 属性赋值 -> 初始化 -> 销毁!
主要逻辑都在doCreate()方法中,逻辑很清晰,就是顺序调用以下三个方法,这三个方法与三个生命周期阶段一一对应,非常重要,在后续扩展接口分析中也会涉及。
1、createBeanInstance() -> 实例化
2、populateBean() -> 属性赋值
3、initializeBean() -> 初始化
在这里插入图片描述
至于销毁,是在容器关闭时调用的,详见ConfigurableApplicationContext#close();


1. 为什么要使用 Spring?

在这里插入图片描述

2. 解释一下什么是 AOP?

项目应用实例(使用AOP实现自定义日志):https://blog.csdn.net/qq_41029923/article/details/120503841
项目应用实例(Redisson分布式锁的实现):https://blog.csdn.net/qq_41029923/article/details/120512744

AOP详情参考:https://blog.csdn.net/qq_33369905/article/details/105828920?spm=1001.2014.3001.5501

SpringAOP底层的实现原理是 JDK动态代理的方式实现:使用 JDK API动态的在内存中创建代理对象;
IOC控制反转用的是 Java的反射机制注入的;
MyBatis为什么没有实现接口的类就可以调用接口中的方法?:MyBatis通过动态代理的方式实现了只通过 mapper接口而无接口的实现类的方式操作数据库;

Spring使用AOP实现事务控制:
配置事务管理器、事务通知、AOP:

<aop:config>
   <aop:pointcut id="pt1" expression="execution(* com.jiading.service.impl.*.*(..))"/>
   <aop:aspect id="txAdvice" ref="txManager">
       <aop:before pointcut-ref="pt1" method="beginTransaction"></aop:before>
       <aop:after pointcut-ref="pt1" method="commit"></aop:after>
       <aop:after-throwing pointcut-ref="pt1" method="rollback"></aop:after-throwing>
  	   <aop:after-returning pointcut-ref="pt1" method="release"></aop:after-returning>
   </aop:aspect>
</aop:config>

详情参考:https://www.cnblogs.com/jiading/p/12368832.html

AOP 用处:事务、日志、异常等
在这里插入图片描述
在软件开发过程中,跨越应用程序多个点的功能称为交叉问题。这些交叉问题与应用程序的主要业务逻辑不同。因此,将这些横切关注与业务逻辑分开是面向方面编程(AOP)的地方。
问题:Spring AOP的代理是什么?
Spring AOP是基于代理实现的,默认为标准的 JDK 动态代理。这使得任何接口(或者接口的集合)可以被代理。
Spring AOP 也使用 CGLIB 代理。如果业务对象没有实现任何接口那么默认使用CGLIB。

3. 解释一下什么是 IOC?

在这里插入图片描述

4. Spring有哪些模块?

在这里插入图片描述

5. Spring常用的注入方式有哪些?

在这里插入图片描述

//一、使用构造方法注入
//有<bean id="" class="">必须要有无参构方
//使用constructor属性标签是时,必须要有带参构方
<bean id="car" class="com.asd.Car">
    <constructor-arg index="0" value="黑色"/>
    <constructor-arg index="1" value="白色"/>

    <constructor-arg name="color" value="黑色"/>
    <constructor-arg name="brand" value="benz"/>
</bean>

//二、使用setter属性注入
//此方式时最常用的;有property属性标签,类中必须有对应的set方法
<bean id="car2" class="com.asd.Car">
    <property name="color" value="黑色"/>
    <property name="brand" value="benz"/>
</bean>

三、使用注解方法注入:
@Component:bean注入
@Respository:持久层dao
@Service:service层
@Controller
@Resource:属性注入

//Controller层
@Controller
@RequestMapping("/admin")
public class AdminController {
    @Autowired
    private AdminServiceImpl adminServiceImpl;

    @RequestMapping("/adminRegist") //管理员注册
    public void adminRegist(Admin admin,HttpServletRequest req,HttpServletResponse resp) throws ServletException, IOException {
        adminServiceImpl.insertInstance(admin);
        req.getRequestDispatcher("/manage_login.jsp").forward(req, resp);
    }
}

//Service层
@Service
public class AdminServiceImpl extends CommonServiceImpl<Admin> {
    @Autowired
    private AdminDao adminDao;

    public void insertInstance(Admin admin) {
        adminDao.insertAdmin(admin);
    }
    public Admin findInstance(int id) {
        return adminDao.findAdmin(id);
    }
}

//Dao层
public interface AdminDao {
    void insertAdmin(Admin admin); //添加管理员信息
    void deleteAdmin(int id); //根据id删除管理员信息
    void updateAdmin(Admin admin) ;//修改管理员信息
}

// xml文件中开启注解扫描
<context:component-scan base-package="com.asd"/>

测试时:AppTest.java中
① 先通过配置文件得到IOC容器 applicationContext;
② 再从容器 applicationContext.getBean(“bean的id值”) 获取 bean对象;

public class AppTest {
    private ApplicationContext applicationContext;
    @Before
    public void init(){
        applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
    }
    @Test
    public void Test1(){
        User user1= (User) applicationContext.getBean("user1");
        System.out.println(user1);
    }
 }

5_1. Spring IOC的三种注入方式:

【1】构造器注入:
【2】setter注入:
【3】接口注入:

//构造器注入
public class A{
    private Admin admin;
    public A(Admin admin){
        this.admin=admin;
    }
}
//setter注入
public class A{
    private Admin admin;
    public void setAdmin(Admin admin){
        this.admin=admin;
    }
}
//接口注入
public interface Admin{
    public void Admin(User user);
}
public class A{
    private User user;
    public void Admin(User user){
        this.user=user;
    }
}

5_2. Spring中的 bean是线程安全吗?

在这里插入图片描述

5_3. Spring中的 bean默认是单例模式吗?

Spring中的 bean默认是单例模式的,配置文件中创建bean对象时可修改。
单例:scope值为 singleton、或者不写时默认单例 ;scope值改为 prototype表示多例;
懒加载:lazy-init 值为 default、false时默认不延迟(懒)加载;lazy-init值为 true时表示启用懒加载;

init-method、destory-method是类中自带的方法的方法名:eg:

<bean id="user" class="com.asd.User" 
      scope="singleton" lazy-init="default" 
      init-method="init_user" destory-method="destory_user"/>

在这里插入图片描述

6. Spring支持几种 bean的作用域?

在这里插入图片描述

7. Spring自动装配 bean的方式有哪些?

在这里插入图片描述

8. Spring容器创建对象的几种方式?

1、调用无参的构造方法;(只要有下面这句,类中就要有无参构方)eg:
<bean id="user" class="com.asd.User"></bean>
2、调用带参的构造方法;

<bean id="car" class="com.asd.Car">
    <constructor-arg index="0" value="黑色"/>
    <constructor-arg index="1" value="白色"/>
    
    <constructor-arg name="color" value="黑色"/>
    <constructor-arg name="brand" value="benz"/>
</bean>

3、使用工厂类创建对象;

//使用工厂类非静态方法创建对象(先创建工厂类的bean实例,再通过id值去引用)
<bean id="userFactory" class="com.asd.UserFactory"/>
<bean id="user1" factory-bean="userFactory" factory-method="getInstance"/>
//使用工厂类静态方法创建对象
<bean id="user2" class="com.asd.UserFactory" factory-method="getStaticInstance"/>

9. Spring中动态代理

代理:可理解为在不修改目标对象的前提下对目标对象进行扩展

【1】静态代理:
要求:代理对象与目标对象实现同样的接口;
优点:可以做到在不修改目标对象的前提下对目标对象进行扩展
缺点:因为代理对象与目标对象实现相同的接口,会有很对代理类,一旦接口中增加方法,目标对象与代理对象都需要维护;
解决方式:用代理工厂即使用动态代理;
【2】动态代理(也叫JDK动态代理、JDK代理、接口代理) 与 cglib代理(也叫子类代理)
动态代理: 目标对象(即类)要实现接口,代理对象不需要实现接口,在内部自动帮忙实现接口,使用到 JDKAPI动态的在内存中创建代理对象(所以又叫接口代理、JDK代理)。使用到 API中 Proxy提供的 newProxyInstance( )方法:三个参数:

target.getClass.getClassLoader(),//目标对象的类加载器
target.getClass.getInterface(),  //目标对象的接口类型 
new InvocationHandler(){  //事件处理器 
    ...
}

【3】 cglib代理: 又叫子类代理,在内存中构建一个子类对象,实现对目标对象的扩展;要求:
① 目标对象(即类)不能是 final,否则会报错;
② 目标对象的方法是 final、static时,则不会拦截此方法(即添加的扩展内容无法得到)

在 SprigAOP编程中:(自动选择)
若加入容器的目标对象有实现接口,用动态代理;
若加入容器的目标对象没有实现接口且目标对象不是 final,用 cglib代理;

详细参考: https://blog.csdn.net/quge_name_harder/article/details/83304287

AspectJ 实现 AOP

注解:@Component、@Aspect;@Before、@After、@AfterReturnning、@AfterThrowing、@Around

【 切面类 】:@Component、@Aspect
【 切入点表达式 】
书写方式:

@Pointcut("execution(public void com.asd.Dao.UserDao.save())")
//@Pointcut("execution(* *.*())")
public void pointCut(){}

方式一:写全部,用execution表达式:@Before(“execution(public void com.asd.Dao.UserDao.save())”)
方式二:直接使用声明好的@Pointcut():@Before(“pointCut()”)

//1、@Before业务代码之前
@Before("execution(public void com.asd.Dao.UserDao.save())")
//@Before("pointCut()")
public void beginTrans(){
	System.out.println("开始事务");
}

//2、@After业务代码之后,无论有无异常都执行
@After("execution(访问修饰 返回类型 类的全限定名 方法名(参数))")
//@After("pointCut()")
public void commitTrans(){
	System.out.println("提交事务");
}

//3、@AfterReturnning当返回结果后作执行,若出现异常不执行此方法
//方式一
@AfterReturnning("execution(* *.*(..))")
public void afterReturnning(){
	System.out.println("AfterReturnning");
}
//方式二 :returning表参数,value表切入点
@AfterReturnning(returning="value",value="pointCut()")
public void returnTest(Object value){
	System.out.println("AfterThrowing");
}

//4、@AfterThrowing出现异常执行此方法
//方式一
@AfterThrowing("execution(* *.*(..))")
public void afterThrowing(){
	System.out.println("AfterThrowing");
}
//方式二 
@AfterThrowing(value="cutPointA()",throwing="e")
public void throwTest(Throwable e){
	System.out.println("异常信息:"+e.toString());
}

//5、@Around此方法有参数,此参数相当于业务对象,同时"joinPoint.proceed()"相当于调用核心方法(即UserDao.save())
@Around("execution(* *.*(..))")
public void round(Proceeding JoinPoint joinPoint)throws Throwable{
	System.out.println("begin");
	joinPoint.proceed();
	System.out.println("end");
}
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值