Spring笔记

Spring

Spring5框架概述

  1. 轻量级(引入jar包少,体积小)、开源的(免费提供源代码)针对J2EE的框架
  2. 目的是为了解决企业应用开发复杂性
  3. 包括控制反转(IOC)面向切面(AOP)

Spring特点

  • 方便解耦,简化开发
  • AOP编程支持
  • 方便程序测试
  • 方便和其他框架整合
  • 方便事务操作
  • 降低API开发难度

IOC容器

将创建对象的过程交给Spring进行管理,这么一个过程称为“控制反转

不用再内部new,而是将常见对象的控制权交给IOC容器实现,用的时候直接拿即可

将对象的创建和对象的调用过程,交给Spring进行管理

AOP

在不修改源代码的情况下,进行功能添加,这样一个功能称为“面向切面

相关jar包文件

https://repo.spring.io/ui/native/release/org/springframework/spring/5.3.22/

  • 创建一个基本的spring需要导入以下几个jar包

    spring-beans

    spring-core

    spring-context

    spring-expression

    commons-logging

IOC简单底层原理

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">
    <bean id="user" class="com.dragon.pojo.User"/>
<!--    <bean id="user" class="com.dragon.pojo.User"/>-->
<!--    <bean id="user" class="com.dragon.pojo.User"/>-->
</beans>
  1. 工厂模式

    public class ClassPathXmlApplicationContext implements ApplicationContext{
        /*
        1.创建IOC容器
            String 对应getBean()中的参数
            Object 对应具体创建的类
        */
        private Map<String,Object> iocContainer = new HashMap<>();
    
        /*
        2.构造器创建
        */
        public ClassPathXmlApplicationContext(String xmlPath){
            /*
            3.Dom4j解析xml配置文件中的bean并返回List集合
                其中Bean是一个封装了xml中bean的id、class的实体类
            */
            List<Bean> listBean = getListBean(xmlPath);
    
            /*
            4.创建对象(反射)
            */
            createObject(listBean);
    
        }
        //这里就是getBean(id)
        @Override
        public Object getBean(String id) {
            return iocContainer.get(id);
        }
    }
    
  2. Dom4J解析XML

    private List<Bean> getListBean(String xmlPath) throws DocumentException{
    
            SAXReader saxReader = new SAXReader();
    //读配置文件
            Document document = saxReader.read("src/main/resources/" + xmlPath);
    //获取根节点元素<beans>
            Element rootElement = document.getRootElement();
    //获取子节点集合
            Iterator<Element> elementIterator = rootElement.elementIterator();
        
            ArrayList<Bean> beans = new ArrayList<>();
    //迭代子节点集合
            while (elementIterator.hasNext()){
                Bean bean = new Bean();
    
                Element next = elementIterator.next();
    //获取bean中的id值
                String id = next.attributeValue("id");
                bean.setId(id);
    //获取bean中的class值
                String clazz = next.attributeValue("class");
                bean.setClassPath(clazz);
                
                beans.add(bBean);
            }
            return beans;
        }
    
  3. 反射机制

    public void createObject(List<Bean> beans){
    
            try{
    
                Iterator<Bean> iterator = beans.iterator();
                while(iterator.hasNext()){
                    Bean bean = iterator.next();
    //获取类路径
                    String classPath = bean.getClassPath();
    //反射机制获取类
                    Class aClass = Class.forName(classPath);
    //实例化
                    Object instance = aClass.newInstance();
    //将id和实例化后的内容放入容器中
                    iocContainer.put(bean.getId(), instance);
                }
    
            }catch (Exception e){
                e.printStackTrace();
            }
    
        }
    

Bean的实例化

1.构造器实例化

2.静态工厂

/**
 * 静态工厂模式
 * (解耦)
 */
public class DaoFactory {
    public static UserDao userDaoFactory(){
        return new UserDao();
    }
}
<bean id="daoFactory" class="com.dragon.factory.DaoFactory" factory-method="userDaoFactory"/>

3.实例化工厂

/**
 * 实例化工厂Bean
 */
public class DaoFactory01 implements FactoryBean<UserDao> {
    @Override
    public UserDao getObject() throws Exception {
        return new UserDao();
    }

    @Override
    public Class<?> getObjectType() {
        return UserDao.class;
    }
}
<bean id="daoFactory01" class="com.dragon.factory.DaoFactory01"/>

Bean的作用域

共有五种作用域

  1. singleton:单例模式,只有一个实例
  2. prototype:全局模式,每次new都会创建一个新的实例
  3. request:每次请求创建实例
  4. session:在httpsession中有效
  5. global-session:全局session

Bean的生命周期

  • 什么是生命周期?

    从对象创建到对象销毁

  • bean的生命周期

    • 初始化容器

      1.创建对象(内存分配),相当于new
      2.执行构造方法
      3.执行属性注入(set操作)
      4.执行bean初始化方法

    • 使用bean

      1.执行业务操作

    • 关闭/销毁容器

      1.执行bean销毁方法

  • 演示bean的生命周期

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           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">
    
        <bean id="bean01" class="com.dragon.pojo.User" init-method="initMethod" destroy-method="destroyMethod">
            <property name="id" value="3"/>
            <constructor-arg ref="teacher"/>
        </bean>
        <bean id="teacher" class="com.dragon.pojo.Teacher"/>
    </beans>
    
    public class TestApplication {
        public static void main(String[] args) {
            /*
            1.创建bean 实例
            2.set注入
            3.初始化构造器
            4.获取bean对象
            5.bean销毁
             */
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean01.xml");
            User bean01 = context.getBean("bean01",User.class);
            System.out.println(bean01.getId());
            bean01.destroyMethod();
        }
    }
    
  • 实现接口InitializingBeanDisposableBean,会默认执行初始化bean和销毁bean

    不过要注意的是,必须使用ClassPathXmlApplicationContext

    同时若执行销毁方法,是有两种执行方法的

    1. ClassPathXmlApplicationContext.close();

      强制关闭,不建议使用

    2. ClassPathXmlApplicationContext.registerShutdownHook();

      推荐使用这种注册关闭,因为当JVM关闭时,第一件事就是先去把容器关闭,再去关闭JVM

    public class UserDao implements InitializingBean, DisposableBean {
        @Override
        public void destroy() throws Exception {
            System.out.println(this.getClass().getName()+"  销毁");
        }
        /**
         * 在属性设置之后
         * @throws Exception
         */
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println(this.getClass().getName() + "  set-init");
        }
        
        private UserService userService;
        public UserDao(){
            System.out.println("构造器初始化");
        }
        public void setUserService(UserService userService){
            System.out.println("-----------set依赖注入");
            this.userService = userService;
        }
        public void selectOne(){
            System.out.println("selectOne");
        }
    }
    /**
    输出结果:
            构造器初始化
            -----------set依赖注入
            com.dragon.dao.UserDao  set-init
            selectOne
            com.dragon.dao.UserDao  销毁
    **/
    

Spring循环依赖

3种形态

  1. 相互依赖

    类A依赖B,类B依赖A

    class A{
        @Autowired
        private B b;
    }
    class B{
        @Autowired
        private A a;
    }
    
  2. 三者依赖

    类A依赖B,类B依赖C,类C依赖A

    class A{
        @Autowired
        private B b;
    }
    class B{
        @Autowired
        private C c;
    }
    class C{
        @Autowired
        private A a;
    }
    
  3. 自身依赖

    类A依赖A

    public class A{
        @Autowired
        private A a;
    }
    

解决循环依赖方法

  • 共有三种缓存

    1. 一级缓存:存放可以被用户使用的bean(被实例化的)
    2. 二级缓存:存放创建好,但没有完全初始化完的属性的bean集合
    3. 三级缓存:存放正在被创建的bean集合,代理bean,比如AOP

DI-注入方式

  • 什么是依赖注入

    在容器中建立bean与bean之间的依赖关系的过程,称为“依赖注入”

  • 三种注入方式

    1. 构造器注入

      <constructor-arg ref="teacher"/>
      
    2. set注入

      <property name="teacher" ref="teacher"/>
      
    3. 注解注入

      使用了反射中的暴力反射

      @Autowired
      private Teacher teacher;
      @Be
      

自动装配

  • 什么是自动装配?

    所谓自动装配就是往IOC容器中以什么样的方式去注入bean

  • 注解使用

    1. @Autowired:默认按照byType装配
    2. @Qualifired:配合AutoWired使用指定Name
    3. @Resource:默认按照name查找,查找不到,按照type查找

装配规则

  1. byName:按照名称自动装配
  2. byType:按照类型自动装配
  3. constructor:构造器中的参数装配

AOP底层原理

AOP是什么

​ 面向切面

为什么会有AOP?

​ 在不改变源代码的基础上,去增加一些新的功能

AOP核心概念

  • 连接点(JoinPoint)

    被拦截的点,指方法

  • 切入点(PointCut)

    被拦截后执行的增强功能,指方法

  • 切面(Aspect)

    描述切入点和通知的对应关系

  • 通知(Advice)

    在切入点执行的操作

  • 通知类

例子:

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(void com.dragon.service.impl.UserServiceImpl.add())")
    private void pt(){

    }
    @Before("pt()")
    public void myAdvice(){
        System.out.println(System.currentTimeMillis());
    }
}

那么在这个操作里面就用到了动态代理,其中包括代理对象和目标对象。

  • 目标对象:指原来的对象,也就是被代理的对象
  • 代理对象:就是替代原来对象的对象,去执行

输出内容:

public class MyApplicationTest {
    @Test
    public void test01(){
        ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
        UserService bean = context.getBean("userServiceImpl", UserService.class);
        System.out.println(bean);
        System.out.println(bean.getClass());
    }
}
/*
bean:           com.dragon.service.impl.UserServiceImpl@776b83cc
bean.getClass():class com.sun.proxy.$Proxy21
*/

动态代理

  • 有接口

    使用JDK动态代理:实现接口代理对象

  • 无接口

    使用CGLIB动态代理:创建子类代理对象

JDK动态代理

使用了java.lang.reflect包中的Proxy类里面的newProxyInstance方法

其中该方法包含三个参数

  1. 类加载器
  2. 类的实现接口
  3. 创建代理对象(实现InvocationHandler接口),写增强部分
  • 代理对象

    public class JdkProxy {
        public static void main(String[] args) {
            //接口
            Class[] interfaces = {UserDao.class};
    
            //被切入的类
            UserDaoImpl userDao = new UserDaoImpl();
    /*
    java.lang.reflect下的Proxy类
    调用newProxyInstance方法去实现AOP操作
    */
            UserDao dao = (UserDao) Proxy.newProxyInstance(
                JdkProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDao));
    
            int res = dao.add(1, 2);
            System.out.println(res);
        }
    }
    
  • 实现InvocationHandler接口

    public class UserDaoProxy implements InvocationHandler {
        private Object obj;
    //构造函数,传入要操作的类
        public UserDaoProxy(Object obj){
            this.obj = obj;
        }
    /*
    proxy:被代理对象
    method:被代理对象的方法Method对象
    args:被代理对象的某方法接收的参数
    */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            System.out.println(method.getName()+"方法执行前");
    
            Object invoke = method.invoke(obj, args);
    
            System.out.println(method.getName()+"方法执行之后");
            return invoke;
        }
    
    }
    

AOP切入点表达式

访问修饰符 返回值类型 包路径.类名.方法名(参数) 异常

  • ..

AOP通知类型

AOP注解

@Aspect

  • @Before

    在被代理目标方法之前执行

  • @After

    在被代理目标方法之后执行

  • @AfterReturning

    在被代理目标方法完成之后执行

  • @AfterThrowing

    在被代理目标方法执行中出现异常执行

  • @Around

@PointCut():抽取方法

@Component
@Aspect
public class UserProxy {
    /**
     * 相同切入点抽取
     */
    //抽取方法
    @Pointcut(value = "execution(* com.dragon.aop.User.add(..))")
    public void commonTest(){

    }

    //调用commonTest()相当于调用了User中的add方法
    @Before(value = "commonTest()")
    public void before(){
        System.out.println("执行之前");
    }

    @After(value = "commonTest()")
    public void after(){
        System.out.println("执行之后");
    }

    @AfterReturning(value = "commonTest()",returning = "obj")
    public void afterReturning(JoinPoint joinPoint,Object obj){
        Signature signature = joinPoint.getSignature();
        System.out.println("afterReturning");
        System.out.println(signature.getName()+obj);
    }

//出现异常抛出
    @AfterThrowing(value = "commonTest()",throwing = "ex")
    public void afterThorowingAdviceMethod(JoinPoint joinPoint,Exception ex){
        //获取连接点所对应方法的签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法:"+signature.getName()+",异常:"+ex);
    }

    @Around(value = "commonTest()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("=====环绕之前======");
        joinPoint.proceed();
        System.out.println("======环绕之后=======");
    }
}

调用目标参数

@Before("ptGetArgs()")
public void myAdvice(JoinPoint jp){
    Object[] args = jp.getArgs();//关键代码
    System.out.println(Arrays.toString(args));
    System.out.println(System.currentTimeMillis());
}

Spring事务

  1. 什么是事务?

例如银行转账,在一个业务类中,有两个数据库的操作,A向B转200,A少200,B多200。这样一个操作就是事务

如若操作成功,即成功,若其中一个失败则会进行事务回滚

  1. 关键注解

@Transactional

该注解最标准的使用应该操作在接口中的方法上或类上,表名该方法中被事务所管理

  1. 设置事务管理器

在配置类中设置,

@Bean
public PlatformTranscationManager transactionManager(DataSource dataSource){
  DataSourceTransactionManager ptm = new DataSourceTransactionManager();
  //此处dataSource为Druid的数据源
  ptm.setDataSource(dataSource);
  return ptm;
}
  1. 开启注解事务驱动

    @EnableTransactionManagement

    操作在配置类上

事务角色

  1. 事务管理员

    就是被 @Transactional操作的方法为事务管理员

  2. 事务协调员

    本来为单个事务,但是因为被事务管理员所管理,所以改成了事务协调员

posted @ 2022-09-02 15:47  DawsonDragon  阅读(22)  评论(0编辑  收藏  举报