Spring笔记

Hello Spring

Spring是一个轻量级开源免费的框架,是非浸入式的,具有控制反转(IOC)和面向切面(AOP)的框架。

Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式 .
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

  1. 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。

  2. Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。

  3. Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。

  4. Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。

  5. Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。

  6. Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。

  7. Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

    使用ClassPathXmlApplicationContext参数为配置文件的名称;
    使用FileSystemXmlApplicationContext参数为配置文件的路径;

     ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
     ApplicationContext context = new FileSystemXmlApplicationContext("E:\\Learn\\Spring\\spring-demo\\kuang\\src\\main\\resources\\beans.xml");
    

    使用getBean()方法获取bean

     Hello hello = context.getBean("hello", Hello.class);
     hello.show();
    

    也可以通过class去获取一个bean,不过有限制,如果容器中有两个相同的class就会报错

     Hello hello = context.getBean(Hello.class);
     hello.show();
    

    所以一般用id获取bean。

IOC

什么是ioc : ioc 就是管理对象的创建和对象间的依赖,之前是完全是硬编码在代码中,现在把这部分转移给第三方,也就是spirng容器中,通过xml或者注解的方式从容器中获取

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

底层实现

ioc底层是通过解析xml的class值,再通过反射来创建对象的

Spring 提供 IOC 容器实现两种方式:(两个接口)

(1)BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用

  • 加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象

(2)ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用

  • 加载配置文件时候就会把在配置文件对象进行创建

依赖注入

  • set方法注入<重点>

    要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写

    <!--set注入《重点》-->
    <bean id="address" class="cn.chuzz.ioc.pojo.Address">
        <!--使用 property 完成属性注入
             name:类里面属性名称
             value:向属性注入的值-->
        <property name="address" value="邯郸市"/>
    </bean>
    
    <bean id="student" class="cn.chuzz.ioc.pojo.Student">
        <!--普通常量注入-->
        <property name="name" value="chuzz"/>
        <!--bean注入-->
        <property name="address" ref="address"/>
        <!--数组注入-->
        <property name="books">
            <array>
                <value>龙族</value>
                <value>遮天</value>
            </array>
        </property>
        <!--List注入-->
        <property name="hobbys">
            <list>
                <value>跑步</value>
                <value>轮滑</value>
                <value>游泳</value>
            </list>
        </property>
        <!--Map注入-->
        <property name="card">
            <map>
                <entry key="工商银行" value="123123"/>
                <entry key="华夏银行" value="123123"/>
            </map>
        </property>
        <!--Set注入-->
        <property name="games">
            <set>
                <value>王者</value>
                <value>联盟</value>
            </set>
        </property>
        <!--null注入-->
        <property name="dog"><null/></property>
        <!--Properties-->
        <property name="info">
            <props>
                <prop key="成绩">100</prop>
                <prop key="学号">520</prop>
            </props>
        </property>
        <property name="flag" value="false"/>
    </bean>
    <!--特殊符号注入注入-->
    <bean id="orders2" class="cn.chuzz.ioc.Orders">
        <!--
                1、把<>进行转义&lt;&gt;
                2、把带符号的内容写到<![CDATA[]]>
            -->
        <property name="name" value="&lt;杨戬&gt;"/>
        <property name="address">
            <value><![CDATA[<<灌江口>>]]></value>
        </property>
    </bean>
    <!--注入内部bean-->
    <bean id="emp" class="cn.chuzz.ioc.entity.Emp">
        <property name="name" value="天明"/>
        <property name="empId" value="007"/>
        <property name="dept">
            <bean id="dept" class="cn.chuzz.ioc.entity.Dept">
                <property name="name" value="墨家"/>
            </bean>
        </property>
    </bean>
    <!--级联赋值(一)-->
    <!--<bean id="emp" class="cn.chuzz.ioc.entity.Emp">
            <property name="name" value="天明"/>
            <property name="empId" value="007"/>
            &lt;!&ndash;级联赋值&ndash;&gt;
            <property name="dept" ref="dept"/>
        </bean>
        <bean id="dept" class="cn.chuzz.ioc.entity.Dept">
            <property name="name" value="墨家"/>
        </bean>-->
    <!--级联赋值(二)-->
    <bean id="emp" class="cn.chuzz.ioc.entity.Emp">
        <property name="name" value="天明"/>
        <property name="empId" value="007"/>
        <!--级联赋值-->
        <property name="dept" ref="dept"/>
        <property name="dept.name" value="剑圣传人"/>
    </bean>
    <bean id="dept" class="cn.chuzz.ioc.entity.Dept">
        <property name="name" value="墨家"/>
    </bean>
    
  • 构造方法注入

    • 无参构造
    • 有参构造
     <!--测试有参构造方法-->
     <bean id="userT" class="cn.chuzz.ioc.pojo.User">
         <!-- 第一种根据index参数下标设置 -->
         <!-- index指构造方法 , 下标从0开始 -->
         <constructor-arg index="0" value="chuzz"/>
         <!-- 第二种根据参数名字设置 -->
         <!-- name指参数名 -->
         <constructor-arg name="name" value="chuzz"/>
         <!-- 第三种根据参数类型设置 -->
         <!--type指参数类型-->
         <constructor-arg type="java.lang.String" value="chuzz"/>
     </bean>
    
  • p命名空间和c命名空间注入

    P(属性: properties)命名空间 , 属性依然要设置set方法
    C(构造: Constructor)命名空间 , 属性依然要设置set方法

    <!--P命名空间注入-->
    <bean id="userP" class="cn.chuzz.ioc.pojo.User" p:name="chuzz"/>
    <!--C命名空间注入-->
    <bean id="userC" class="cn.chuzz.ioc.pojo.User" c:name="chuzz"/>
    

bean的作用域

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。
简单地讲,bean就是由IoC容器初始化、装配及管理的对象。

  • Singleton 单例类型(默认)。

    当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,
    并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。
    Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,
    他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。
    要在XML中将bean定义成singleton,可以这样配置:

    <bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">
    
  • Prototype 原型类型

    当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。
    Prototype作用域的bean会导致在每次对该bean请求
    (将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。
    Prototype是原型类型,它在我们创建容器的时候并没有实例化,
    而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。
    根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
    在XML中将bean定义成prototype,可以这样配置:

    <bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>  
    <!--或者-->
    <bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
    

设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象
设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建 对象,在调用getBean 方法时候创建多实例对象

  • Request

    当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,
    它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。
    考虑下面bean定义:

        <bean id="loginAction" class="cn.csdn.LoginAction" scope="request"/>
    

    针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,
    且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,
    而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,
    request作用域的bean实例将被销毁。

  • Session

    当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。
    该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

    <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
    

    针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,
    且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,
    而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。
    当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。

几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),
只能用在基于web的Spring ApplicationContext环境。

bean的生命周期

​ 从对象创建到对象销毁的过程叫生命周期

bean的生命周期

  1. 通过构造器创建bean实例(无参构造)

  2. 为bean的属性设置值以及对其他bean的引用(调用属性的set方法)

  3. 调用bean的初始化方法(需要进行配置初始化方法)

  4. 可以对bean进行操作(bean可以使用了)

  5. 当容器关闭时,调用bean的销毁方法

    public class Orders {
        private String name;
        private String address;
        public Orders() {
            System.out.println("第一步  执行无参构造函数创建bean实例");
        }
        public Orders(String name, String address) {
            this.name = name;
            this.address = address;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            System.out.println("第二步  调用set方法给属性赋值");
            this.name = name;
        }
        public String getAddress() {
            return address;
        }
        public void setAddress(String address) {
            this.address = address;
        }
        public void initMethod(){
            System.out.println("第三步  调用初始化方法");
        }
        public void destroyMethod(){
            System.out.println("第五步  调用销毁方法");
        }
    }
    <bean id="orders2" class="cn.chuzz.ioc.entity.Orders" init-method="initMethod" destroy-method="destroyMethod">
        <property name="name" value="&lt;杨戬&gt;"/>
        <property name="address">
        	<value><![CDATA[<<灌江口>>]]></value>
        </property>
    </bean>
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            /*注入特殊符号*/
            Orders orders2 = context.getBean("orders2", Orders.class);
            System.out.println(orders2);
            System.out.println("第四步  bean实例可以使用了");
            context.close();
    
    输出
        D:\Software\java\jdk1.8\bin\java.exe "-javaagent:D:\Software\IntelliJ IDEA 
    第一步  执行无参构造函数创建bean实例
    第二步  调用set方法给属性赋值
    第三步  调用初始化方法
    Orders{name='<杨戬>', address='<<灌江口>>'}
    第四步  bean实例可以使用了
    第五步  调用销毁方法
    
    Process finished with exit code 0
    

bean的自动装配

根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入

xml配置

  • 自动装配是使用spring满足bean依赖的一种方法
  • spring会在应用上下文中为某个bean寻找其依赖的bean

Spring的自动装配需要从两个角度来实现,或者说是两个操作:

  1. 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;

  2. 自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;

组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少。

  • autowire byName (按名称自动装配)

    由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。

    采用自动装配将避免这些错误,并且使配置简单化。

    修改bean配置,增加一个属性 autowire=“byName”

    小结

    1. 当一个bean节点带有 autowire byName的属性时。

    2. 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。

    3. 去spring容器中寻找是否有此字符串名称id的对象。如果有,就取出注入;如果没有,就报空指针异常。

  • autowire byType (按类型自动装配)

    使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。

    将user的bean配置修改一下 : autowire=“byType”

使用注解实现自动装配

jdk1.5开始支持注解,spring2.5开始全面支持注解。
准备工作:利用注解的方式注入属性。

开启属性注解支持

 <context:annotation-config/>

注解实现

使用注解实现

<context:component-scan base-package="cn.chuzz.ioc.pojo"/>

增加注解@Component表示该类是一个组件

(1)@Component
(2)@Service
(3)@Controller
(4)@Repository

  • 上面四个注解功能是一样的,都可以用来创建 bean 实例
@Repository
public class DeptDaoImpl implements IDeptDao {
    @Override
    public void add() {
        System.out.println("DeptDaoImpl add.....");
    }
}
@Service
public class DeptServiceImpl implements IDeptService {

    @Autowired
    private IDeptDao deptDao;

    @Override
    public void add() {
        deptDao.add();
        System.out.println("DeptServiceImpl add.....");
    }
}
public static void main(String[] args){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    IDeptService deptService = context.getBean("deptServiceImpl", DeptServiceImpl.class);
    deptService.add();
}
//输出
DeptDaoImpl add.....
DeptServiceImpl add.....

context:component-scan 配置细节

use-default-filters=“false” 表示现在不使用默认 filter,自己配置 filter
context:include-filter ,设置扫描哪些内容

<!--表示只加载Repository注解-->
<context:component-scan base-package="cn.chuzz.ioc" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
<!--表示只加载除了Repository注解之外的-->
<context:component-scan base-package="cn.chuzz.ioc">
    <context:exclude-filter expression="org.springframework.stereotype.Repository" type="annotation"/>
</context:component-scan>

java类进行配置

新建一个config配置包,编写一个MyConfig配置类

@Configuration  //代表这是一个配置类
@Import(MyConfig2.class)  //导入合并其他配置类,类似于配置文件中的 import 标签
@ComponentScan(basePackages = {"cn.chuzz.ioc"})// 扫描
public class MyConfig {

   @Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
   public Dog dog(){
       return new Dog();
  }
}
/*编写测试类*/
@Test
public void testAnnotation(){
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    IDeptService deptService = context.getBean("deptServiceImpl", DeptServiceImpl.class);
    deptService.add();
}

输出

输出

关于扫描

使用excludeFilters时,useDefaultFilters = true,

使用includeFilters时,useDefaultFilters = false,

代理模式

  • 静态代理

    静态代理角色分析

    • 抽象角色 : 一般使用接口或者抽象类来实现

    • 真实角色 : 被代理的角色

    • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .

    • 客户 : 使用代理角色来进行一些操作 .

  • 动态代理

    • 动态代理的角色和静态代理的一样 .

    • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

    • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

      • 基于接口的动态代理----JDK动态代理
      • 基于类的动态代理----cglib
    • 一个动态代理 , 一般代理某一类业务

    • 一个动态代理可以代理多个类,代理的是接口!

JDK的动态代理需要了解两个类

核心 : InvocationHandler 和 Proxy

【InvocationHandler:调用处理程序】

  • InvocationHandler是由代理实例的调用处理程序实现的接口

    每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

在这里插入图片描述
【Proxy : 代理】
图片

图片

根据jdk源码编写公共的动态代理类

/**
 * 动态代理  代理角色
 */
public class ProxyInvocationHandler implements InvocationHandler {

    /**
     * 被代理的真实角色
     */
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    //生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
    public Object getProxy(){

        /**
         * Object newProxyInstance(ClassLoader loader,
         *                                       Class<?>[] interfaces,
         *                                       InvocationHandler h)
         * 参数
         * loader - 类加载器来定义代理类
         * interfaces - 代理类实现的接口列表
         * h - 调度方法调用的调用处理函数
         */
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    /**
     * 调用处理程序
     * @param proxy 代理类
     * @param method 代理类的调用处理程序的方法对象.
     * @param args 方法中的参数
     * @return 处理代理实例上的方法调用并返回结果
     * @throws Throwable 异常
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //seeHouse();
        Object result = method.invoke(target, args);
        //fare();
        return result;
    }
}

AOP

不改变原先业务代码的情况下,横切进去一段逻辑,对原有功能进行增强,比如事务、日志等功能

什么是AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

Aop底层原理是通过动态代理实现

Aop在Spring中的作用

提供声明式事务;允许用户自定义切面

以下名词需要了解下:

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …

    对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点

  • 连接点(JointPoint):与切入点匹配的执行点(哪些方法可以被增强,这些方法称为连接点)。

  • 切入点(PointCut):切面通知 执行的 “地点”的定义(实际被增强的方法)。

  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法(增强的逻辑部分称为通知)。

  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类(是一个动作,把通知实际应用到切入点的过程称为切面)。

  • 目标(Target):被通知对象。

  • 代理(Proxy):向目标对象应用通知之后创建的对象。

图片

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

图片

即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .

使用Spring实现Aop

【重点】使用AOP织入,需要导入一个依赖包!

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.6</version>
</dependency>

切入点表达式

切入点表达式

通过 Spring API 实现

/**
 * before 前置
 * after 后置
 */
public class Log implements MethodBeforeAdvice, AfterReturningAdvice {
    /**
     * 前置增强
     * @param method 执行的方法
     * @param args 参数
     * @param target 目标对象
     * @throws Throwable 异常
     */
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(method.toString());
        System.out.println(args.toString());
        System.out.println(target.toString());
        System.out.println( target.getClass().getName() + "的" + method.getName() + "方法被执行了");
    }

    /**
     * 后置增强
     * @param returnValue 返回值
     * @param method 执行的方法
     * @param args 参数
     * @param target 目标对象
     * @throws Throwable 异常
     */
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {

        System.out.println("执行了" + target.getClass().getName()
                +"的"+method.getName()+"方法,"
                +"返回值:"+returnValue);
    }
}
<bean id="userService" class="cn.chuzz.aop.service.impl.UserServiceImpl"/>
<bean id="log" class="cn.chuzz.aop.Log"/>
<!--aop的配置-->
<aop:config>
    <!--切入点 expression:表达式匹配要执行的方法-->
    <aop:pointcut id="pointcut" expression="execution(* cn.chuzz.aop.service.impl.UserServiceImpl.*(..))"/>
    <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
    <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
</aop:config>

Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理 .

自定义类来实现Aop

public class DiyPointcut {
    public void before(){
        System.out.println("--------方法执行前---------");
    }
    public void after(){
        System.out.println("--------方法执行后---------");
    }
}
<bean id="diy" class="cn.chuzz.aop.DiyPointcut"/>
<!--aop的配置-->
<aop:config>
    <!--第一种方式-->
    <!--切入点 expression:表达式匹配要执行的方法-->
    <!--<aop:pointcut id="pointcut" expression="execution(* cn.chuzz.aop.service.impl.UserServiceImpl.*(..))"/>-->
    <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
    <!--<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>-->

    <!--第二种方式,使用aop的标签实现-->
    <aop:aspect ref="diy" id="aa" order="1">
        <aop:pointcut id="diyPointcut" expression="execution(* cn.chuzz.aop.service.impl.UserServiceImpl.*(..))"/>
        <!--增强的方法织入到切入点上-->
        <aop:before method="before" pointcut-ref="diyPointcut"/>
        <aop:after-returning method="afterReturning" pointcut-ref="diyPointcut"/>
        <aop:after method="after" pointcut-ref="diyPointcut"/>
        <aop:around method="around" pointcut-ref="diyPointcut"/>
        <aop:after-throwing method="afterThrowing" pointcut-ref="diyPointcut"/>
    </aop:aspect>
</aop:config>

使用注解实现

定义一个切面类

@Aspect
public class MyAspect {
    @Pointcut("execution(* cn.chuzz.aop.service.*.*(..))")
    public void pointcut(){

    }

    /**
     * 前置通知
     */
    @Before("pointcut()")
    public void before(){
        System.out.println("方法执行前(前置通知)------");
    }

    /**
     * 后置通知
     * 正常返回
     */
    @AfterReturning("pointcut()")
    public void afterReturning(){
        System.out.println("方法执行后(后置通知-正常返回通知)-----");
    }

    /**
     * 最终通知
     * 异常和正常都会通知
     */
    @After("pointcut()")
    public void after(){
        System.out.println("最终通知(后置通知-异常和正常都会通知)--------");
    }

    /**
     * 环绕通知
     * @param pjp 连接点信息
     * @return 返回
     * @throws Throwable 异常
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知--- 前 --------");
        Object proceed = pjp.proceed();
        System.out.println("环绕通知--- 后 --------");
        return proceed;
    }

    /**
     * 异常通知
     */
    @AfterThrowing("pointcut()")
    public void afterThrowing(){
        System.out.println("异常通知---------");
    }
}

配置文件中定义如下

<!--第三种方式:注解实现-->
<bean id="myAspect" class="cn.chuzz.aop.MyAspect"/>
<!--开启Aspect生成代理对象-->
<aop:aspectj-autoproxy/>

JdbcTemplate

​ Spring框架对jdbc进行封装,使用jdbcTemplate方便对数据库进行操作。

  1. 第一步引入jdbc相关依赖

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.15.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.25</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.6</version>
    </dependency>
    
  2. 配置jdbcTemplate和连接池

    <!--配置数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/dev"/>
        <property name="username" value="root"/>
        <property name="password" value=""/>
    </bean>
    <!--配置jdbcTemplate,注入连接池-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--扫描注解-->
    <context:component-scan base-package="cn.chuzz.jdbcTemplate"/>
    
  3. 使用jdbcTemplate操作数据库

    1. 新增、修改、删除

    2. 查询返回某个值

    3. 查询返回对象

    4. 查询返回列表

    5. 批量新增、修改、删除

      代码如下

      //dao层
      @Repository
      public class UserDaoImpl implements IUserDao {
          @Autowired
          private JdbcTemplate jdbcTemplate;
          //新增修改删除
          @Override
          public int cudUser(String operType) {
              String sql ;
              Object[] args ;
              switch (operType){
                  case "c":
                      sql = "insert into user(name,sex,phone,address) values (?,?,?,?)";
                      args = new Object[]{"张三", "0", "110", "xxx"};
                      break;
                  case "u":
                      sql = "update user set name = ? where id = ?";
                      args = new Object[]{"天明", "3"};
                      break;
                  case "d":
                      sql = "delete from user where id = ?";
                      args = new Object[]{"3"};
                      break;
                  default:
                      throw new IllegalStateException("Unexpected value: " + operType);
              }
              int row = jdbcTemplate.update(sql, args);
              System.out.println(row);
              return row;
          }
          //查询返回某值
          @Override
          public String getUserName(int id) {
              String sql = "select name from user where id = ?";
              String s = jdbcTemplate.queryForObject(sql, String.class, id);
              return s;
          }
          //查询返回对象
          @Override
          public String getUser(int id) {
              String sql = "select * from user where id = ?";
              User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), id);
              System.out.println(user);
              return null;
          }
          //查询返回列表
          @Override
          public List<User> findUserList() {
              String sql = "select * from user";
              return jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class));
          }
          //批量新增修改删除
          @Override
          public int batchCudUser(String operType) {
              List<Object[]> batchArgs = new ArrayList<>();
              String sql ;
              Object[] args ;
              switch (operType){
                  case "c":
                      sql = "insert into user(name,sex,phone,address) values (?,?,?,?)";
                      args = new Object[]{"张三", "0", "110", "xxx"};
                      break;
                  case "u":
                      sql = "update user set name = ? where id = ?";
                      args = new Object[]{"天明", "3"};
                      break;
                  case "d":
                      sql = "delete from user where id = ?";
                      args = new Object[]{"3"};
                      break;
                  default:
                      throw new IllegalStateException("Unexpected value: " + operType);
              }
              batchArgs.add(args);
              int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
              System.out.println(Arrays.toString(ints));
              return 0;
          }
      }
      
      //service层
      @Service
      public class UserServiceImpl implements IUserService {
          @Autowired
          private IUserDao userDao;
          // 新增修改删除
          @Override
          public int cudUser(String operType) {
              return userDao.cudUser(operType);
          }
          // 查询返回某值
          @Override
          public String getUserName(int id) {
              return userDao.getUserName(id);
          }
          // 查询返回对象
          @Override
          public String getUser(int id) {
              return userDao.getUser(id);
          }
          // 查询返回对象
          @Override
          public List<User> findUserList() {
              return userDao.findUserList();
          }
          //批量新增修改删除
          @Override
          public int batchCudUser(String operType) {
              return userDao.batchCudUser(operType);
          }
      }
      
      //测试
      ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
      IUserService userService = context.getBean("userServiceImpl", IUserService.class);
      // 新增修改删除
      //userService.cudUser("u");
      // 查询返回某值
      //String userName = userService.getUserName(1);
      //System.out.println(userName);
      //查询返回对象
      // userService.getUser(4);
      // 查询返回列表
      //System.out.println("userService.findUserList() = " + userService.findUserList());
      //批量新增修改删除
      //userService.batchCudUser("c");
      

事务

事务是把一系列的操作当成一个独立的工作单元,这些操作要么全部成功,要么完全不起作用

事务的四个属性

  • 原子性(atomicity)

    事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用

  • 一致性(consistency)

    事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。这种特性称为事务的一致性。假如数据库的状态满足所有的完整性约束,就说该数据库是一致的。

  • 隔离性(isolation)

    可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏

  • 持久性(durability)

    持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

声明式事务-xml

配置事务管理器

Spring的核心事务管理抽象,管理封装了一组独立于技术的方法

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--注入数据源-->
    <property name="dataSource" ref="dataSource"/>
</bean>

配置事务通知

<!--配置事务-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!--配置哪些方法需要加事务-->
        <tx:method name="testTx"/>
    </tx:attributes>
</tx:advice>

配置aop织入事务

<aop:config>
    <aop:pointcut id="pointcut" expression="execution(* cn.chuzz.jdbcTemplate.service..*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>

部分代码

//service层
@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    private IUserDao userDao;
    @Override
    public int testTx(String operType) {
        userDao.cudUser(operType);
        int i = 10/0;
        userDao.batchCudUser(operType);
        return 0;
    }
}
public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    IUserService userService = context.getBean("userServiceImpl", IUserService.class);
    userService.testTx("c");
}

声明式事务-注解

配置事务管理器&开启事务注解

<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--注入数据源-->
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>

@Transactional

@Service("userServiceJdbcTemplate")
public class UserServiceImpl implements IUserService {
    @Autowired
    private IUserDao userDao;
    /**
     * 转账
     */
    @Override
    @Transactional// 该注解可以在方法上,也可以注解在类上,注解在类上表示类中所有方法都开启事务
    public void account() {
        userDao.addMoney();//加钱
        int i = 10/0;// 模拟异常
        userDao.reduceMoney();//减钱
    }
}
@Repository
public class UserDaoImpl implements IUserDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Override
    public void addMoney() {
        String sql = "update user set money = money + ? where id = ?";
        jdbcTemplate.update(sql,100,3);
    }

    @Override
    public void reduceMoney() {
        String sql = "update user set money = money - ? where id = ?";
        jdbcTemplate.update(sql,100,4);
    }
}

事务的参数配置

propagation 事务的传播行为

事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:

  • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择,也是事务默认的。
  • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。

ioslation:事务隔离级别

事务有特性成为隔离性,多事务操作之间不会产生影响。

如果不考虑隔离性产生很多问题 ,比如

  • 脏读:一个未提交事务读取到另一个未提交事务的数据

  • 不可重复读:一个未提交事务读取到另一提交事务修改数据

  • 虚(幻)读:一个未提交事务读取到另一提交事务添加数据

解决:通过设置事务隔离级别,解决读问题

timeout:超时时间

事务需要在一定时间内进行提交,如果不提交进行回滚 ,默认值是 -1 ,设置时间以秒单位进行计算

readOnly:是否只读

读:查询操作,写:添加修改删除操作

readOnly 默认值 false,表示可以查询,可以添加修改删除操作

设置 readOnly 值是 true,设置成 true 之后,只能查询

rollbackFor:回滚

设置出现哪些异常进行事务回滚

noRollbackFor:不回滚

设置出现哪些异常不进行事务回滚

Spring 配置文件

  • alias 别名

     <!--设置别名:在获取Bean的时候可以使用别名获取-->
     <alias name="userT" alias="userNew"/>
    
  • bean

    bean就是java对象,由Spring创建和管理,常用标签有:

    1. id 是bean的唯一标识符,如果没有配置id,name就是默认标识符

    2. name属性也可以标记bean,可以设置多个名称,用逗号、分号、空格隔开

      如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;

      如果配置id,又配置了name,那么name是别名

    3. class是bean的全限定名=包名+类名

    4. init-method=“init” bean的初始化方法,在bean初始化的时候调用

    5. destroy-method=“destory” bean的销毁方法,在容器销毁的时候调用

    6. scope bean的作用域

      常用子标签有

      1. property

        给bean中属性赋值,name:属性名称,value:要赋的值 ,ref:引入外部bean

        bean中必须有该属性的set方法

        其余类型的赋值参考IOC的依赖注入在这里插入图片描述

      2. constructor-arg

        构造方法赋值 index:构造方法中参数下标,value:参数值;name:参数名称,value:参数值;type:参数类型,valule:参数值

    7. autowire

      autowire属性常用两个值

      ​ byName 根据属性名称注入,bean的id和要注入的属性名称一样

      ​ byType 根据属性类型注入,bean的类型要和注入的属性类型一样,并且只能有一个,否则报错

    <!--bean就是java对象,由Spring创建和管理-->
    <bean id="hello" class="cn.chuzz.ioc.pojo.Hello">
        <property name="name" value="Spring"/>
    </bean>
    
  • description

    描述、备注、说明

    <description>beans配置文件</description>
    
  • import

    开发团队中其他同事的配置文件通过import引入到总配置中

    <import resource="beans.xml"/>
    
  • context

    上下文

    • annotation-config

      当我们需要使用BeanPostProcessor时,直接在Spring配置文件中定义这些Bean显得比较笨拙,例如:
      使用@Autowired注解,必须事先在Spring容器中声明AutowiredAnnotationBeanPostProcessor的Bean:

      <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor "/>
      

      类似地,使用@Resource、@PostConstruct、@PreDestroy等注解就必须声明 CommonAnnotationBeanPostProcessor;使用@PersistenceContext注解,就必须声明 PersistenceAnnotationBeanPostProcessor的Bean。

      这样的声明未免太不优雅,而Spring为我们提供了一种极为方便注册这些BeanPostProcessor的方式,

      即使用<context:annotation- config/>隐式地向 Spring容器注册BeanPostProcessor

      开启注解支持

      <context:annotation-config/> 
      
    • component-scan

      在xml配置了这个标签后,spring可以自动去扫描base-package下面或者子包下面的java文件,如果扫描到有@Component @Controller@Service等这些注解的类,则把这些类注册为bean

      注意:如果配置了context:component-scan那么context:annotation-config/标签就可以不用再xml中配置了,因为前者包含了后者。

      有以下属性

      • base-package

        指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。

      • name-generator

        指定产生规则

      • use-default-filters=“false”

        表示不要使用默认的过滤器,
        此处的默认过滤器,会扫描包含Service,Component,Repository,Controller注解修饰的类,
        配合两个子标签进行使用

        1. context:include-filter 指定的扫描

          这样就会只扫描base-package指定下的有@Controller下的java类,并注册成bean

          <context:component-scan base-package="tv.huan.weisp.web" use-default-filters="false">
              <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
          </context:component-scan>
          
        2. context:exclude-filter 指定的不扫描

        base-package指定的包中有的子包是不含有注解了,所以不用扫描,此时可以指定context:exclude-filter来进行过滤,说明此包不需要被扫描。

      组件扫描,对类包进行扫描以实施注释驱动 Bean 定义的功能
      还启用了注释驱动自动注入的功能(即还隐式地在内部注册了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor),因此当使用 context:component-scan/ 后,就可以将 context:annotation-config/ 移除了。 context:component-scan/ 的 base-package 属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。

    • load-time-waver

      .类加载期间织入切面。如何使用Load Time Weaving?首先,需要通过JVM的-javaagent参数设置LTW的织入器类包,以代理JVM默认的类加载器;第二,LTW织入器需要一个 aop.xml文件,在该文件中指定切面类和需要进行切面织入的目标类。

    • mbean-export

      对于一个普通的java类,作为MBean 需要被管理,通过注解指定需要暴露的属性和方法。

    • mbean-server

      使用jms技术,资源被一种叫做Mbeans监控,这些Mbean都在核心对象管理的server上注册

    • property-override

      属性文件指定的信息可以直接覆盖spring xml配置文件的元数据

    • property-placeholder

      用来处理用一个proerties文件里面的内容来替换spring配置文件中的${}内容

      <context:property-placeholder location="aa.properties"/>
      
    • spring-configured

      spring可以为ioc容器进行依赖注入:但某些类没有配置在ioc中,也可以进行依赖注入。

  • aop

    aop标签

    • aop:scoped-proxy 暴露配置该标签的bean,使其符合自身作用域。

    • aop:aspectj-autoproxy 自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。

      <aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class=“true”/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。

    • aop:config 声明开始的aop配置

    • aop:pointcut 用于配置切入点表达式

      <!--
      	id : 用于给切入点表达式提供一个唯一标识
      	expression : 用于定义切入点表达式。
      		其中 execution()(最常用的方法):是方法运行,可以有多个,中间用 && 里连接 
              	*	是任意返回值,可以有返回值,也可以是void没有返回值的方法
             		cn.dao.IUserDAO.*	是指定目录下的指定类任意方法
              	cn.dao.IUserDAO.insert*	是指定目录下的指定类insert开头的任意方法
              	cn.dao.IUserDAO.*.*	是指定目录下的任意类下的任意方法
              	cn.dao..*.*	是指定目录下的任意目录下任意类下的任意方法
              	(..)	是任何参数,可以是没有参数
      -->
      <aop:pointcut id="diyPointcut" expression="execution(* cn.chuzz.aop.service.impl.UserServiceImpl.*(..))"/>
      

      常见的切面表达式

      1 所有公有方法的执行

      execution(public * *(…))
      2 所有以set开头的公有方法的执行

      execution(* set*(…))
      3 AccountService接口下的所有方法的执行

      execution(* com.xyz.service.AccountService.*(…))
      4 com.xyz.service包下的所有方法的执行

      execution(* com.xyz.service..(…))
      5 com.xyz.service包及其子包下的所有方法的执行

      execution(* com.xyz.service….(…))
      6 匹配com.xyz.service包下的所有类的所有方法(不含子包)

      within(com.xyz.service.*)
      7 com.xyz.service包和子包的所有方法

      within(com.xyz.service…*)
      8 匹配AccountService的代理类(不支持通配符)

      this(com.xyz.service.AccountService)

    • aop:advisor 定义通知器(通知器跟切面一样,也包括通知和切点)

      advice-ref 引入的bean必须要实现advice的接口,用于标记某个类是Advice

      public class Log implements MethodBeforeAdvice, AfterReturningAdvice {}
      
    ```
    • aop:aspect 用于定义切面

      <!-- id  给切面提供一个唯一标识
      - ref  引用配置好的配置类的id-->
      <aop:aspect ref="diy"></aop:aspect>
      
      • aop:before 用于配置前置通知

        <!--
        	method:指定通知中方法的名称。
            pointct:定义切入点表达式
            pointcut-ref:指定切入点表达式的引用
        -->
        <aop:before pointcut-ref="diyPointcut" method="before"/>
        
      • aop:after 用于配置后置通知,不管出不出异常,调用的切面的方法

        <!--
        	method:指定通知中方法的名称。
            pointct:定义切入点表达式
            pointcut-ref:指定切入点表达式的引用
        -->
        <aop:after pointcut-ref="diyPointcut" method="after"/>
        
      • aop:after-returning 用于配置后置通知,如果出了异常就一定不会调用切面的方法

        属性:

        method:指定通知中方法的名称。

        pointct:定义切入点表达式

        pointcut-ref:指定切入点表达式的引用

      • aop:after-throwing 用于配置异常通知,只有出了异常才会调用切面对应的方法

        属性:

        method:指定通知中方法的名称。

        pointct:定义切入点表达式

        pointcut-ref:指定切入点表达式的引用

      • aop:around 用于配置环绕通知

        属性:

        method:指定通知中方法的名称。

        pointct:定义切入点表达式

        pointcut-ref:指定切入点表达式的引用

    aop:aspect与aop:advisor的区别:

    1、实现方式不同
    < aop:aspect>定义切面时,只需要定义一般的bean就行,而定义< aop:advisor>中引用的通知时,通知必须实现Advice接口。

    2、使用场景不同
    < aop:advisor>大多用于事务管理。< aop:aspect>大多用于日志,缓存

    < aop:advisor>和< aop:aspect>其实都是将通知和切面进行了封装,原理基本上是一样的,只是使用的方式不同而已。

  • tx

    • tx:advice 配置事务 ,有id和transaction-manager属性。 id是该advice bean的标识,而transaction-manager则必须引用一个PlatformTransactionManager bean。

      • tx:attributes

        • tx:method

          属性类型默认值说明
          name方法名的匹配模式,通知根据该模式寻找匹配的方法。

          该属性可以使用asterisk (*)通配符
          propagationPropagation枚举REQUIRED事务传播属性
          isolationisolation枚举DEFAULT(所用数据库默认级别)事务隔离级别
          readOnlybooleanfalse是否才用优化的只读事务
          timeoutint-1超时(秒)
          rollbackForClass[]{}需要回滚的异常类
          rollbackForClassNameString[]{}需要回滚的异常类名
          noRollbackForClass[]{}不需要回滚的异常类
          noRollbackForClassNameString[]{}不需要回滚的异常类名

          事务的传播行为

          事务的隔离级别

    • tx:annotation-driven 开启事务注解,有transaction-manager属性,需要引入指定的一个事务管理器

    • tx:jta-transaction-manager

  • util 使用util标签实现对公共部分的提取

    <util:list id="bookList"> 
     <value>易筋经</value>
     <value>九阴真经</value>
     <value>九阳神功</value>
    </util:list>
    <bean id="book" class="com.atguigu.spring5.collectiontype.Book">
     <property name="list" ref="bookList"></property>
    </bean>
    

Sping 注解

  • @Autowired

    可以写在属性上,也可以写在set方法上,通过类型自动装配,如果spring容器中没有注册这个bean的话,会报错
    org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ‘cn.chuzz.ioc.pojo.Cat’
    如果对象可以为空的话,则需在注解上加个参数@Autowired(required=false)required默认为true。如下配置

    //如果允许对象为null,设置required = false,默认为true
    @Autowired(required = false)
    private Cat cat;
    
  • @Qualifier

    • @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配

    • @Qualifier不能单独使用。

    <bean id="dog1" class="com.kuang.pojo.Dog"/>
    <bean id="dog2" class="com.kuang.pojo.Dog"/>
    <bean id="cat1" class="com.kuang.pojo.Cat"/>
    <bean id="cat2" class="com.kuang.pojo.Cat"/>
    
     @Autowired
     @Qualifier(value = "cat2")
     private Cat cat;
     @Autowired
     @Qualifier(value = "dog2")
     private Dog dog;
    
  • @Resource

    • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;

    • 其次再进行默认的byName方式进行装配;

    • 如果以上都不成功,则按byType的方式自动装配。

    • 都不成功,则报异常。

小结

@Autowired与@Resource异同:

  1. @Autowired与@Resource都可以用来装配bean。都可以写在属性上,或写在setter方法上。

  2. @Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用

  3. @Resource(属于J2EE规范),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。

  • @Component

    注入服务:当扫描的时候被注解的类会实例化到Spring容器中。

    @Component三个衍生注解

    为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。

    • @Controller:web层

    • @Service:service层

    • @Repository:dao层

    写上这些注解,就相当于将这个类交给Spring管理装配了!

  • @Value

    给属性赋值,可以写在属性上,可以写在set方法上

      @Component("user")
      // 相当于配置文件中 <bean id="user" class="当前注解的类"/>
      public class User {
         @Value("chuzz")
         // 相当于配置文件中 <property name="name" value="chuzz"/>
         public String name;
         @Value("chuzz")
         public void setName(String name) {
            this.name = name;
         }
      }
    
  • @scope

    作用域

    • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
    • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
      @Component("user")
      @Scope("prototype")
      public class User {
         @Value("chuzz")
         public String name;
      }
    
  • @Configuration 代表该类为一个配置类

  • @Bean 通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!

  • @Import(MyConfig2.class) 导入合并其他配置类,类似于配置文件中的 import 标签

  • @EnableAspectJAutoProxy 启用Aspect生成代理对象

  • @EnableTransactionManagement //开启事务

  • @Aspect 代表该类为一个切面类

  • @Pointcut(“execution(* cn.chuzz.aop.service.impl.UserServiceImpl.*(…))”)

    表示切入点(非浸入式)

  • @Before(“pointcut()”) 前置通知

  • @AfterReturning(“pointcut()”) 后置通知–正常返回通知

  • @After(“pointcut()”) 最终通知–异常和正常返回都会通知

  • @AfterThrowing(“pointcut()”) 异常通知

  • @Around(“pointcut()”) 环绕通知

  • @Order(1) 设置增强类的优先级

    在aop中,如果有多个通知同时增强一个类,则会按照顺序增强,数字类型值越小,优先级越高

  • @Transactional 事务 该注解可以在方法上,也可以注解在类上,注解在类上表示类中所有方法都开启事务

  • @Nullable 注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空

报错

切面表达式异常:Caused by: java.lang.IllegalArgumentException: warning no match for this type name: cn.chuzz.jdbcTemplate [Xlint:invalidAbsoluteTypeName]



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值