spring复习


前言

用来做笔记而已。试试博客啥样;




一、什么是spring

        一种轻量级的java开发框架。核心是控制反转(ioc)和面向切面编程(aop);

        主要作用是为了解耦和,降低代码之间的耦合度。模块之间的关系通过配置文件进行说明。

        优点:轻量,通过ioc降低对象之间的依赖关系,由容器管理对象;面向切面编程,从繁杂的事务当中解脱。


二、控制反转(ioc)

        控制反转是一种思想。将传统意义上由程序员直接操纵的对象的调用权交给容器,由容器进行对象的创建,属性赋值和管理。

控制:创建对象,完成对象的属性赋值和依赖管理

反转:将开发人员直接管理对象的权利交给代码之外的容器实现

传统正转:通过new 的方式进行对象的创建,主动管理对象

public void main(String[] args) {
    MyTest test = new MyTest();
}

javaweb中的ioc体现:创建类继承HttpServlet的时候,在xml文件中完成servlet的注册。通过<servlet-name>寻找<servlet-class>进行创建,这里使用ioc的思想,将servlet对象的创建交付给容器tomcat进行创建。


1.第一个spring程序

- 添加spring依赖:spring-context

- spring配置文件,一般命名为applicationContext.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="Test1" class="com.righteye.impl.SomeServiceImpl" scope="prototype"></bean>
    <bean id="Test2" class="com.righteye.impl.otherServiceImpl"></bean>

    <!--创建非自定义对象-->
    <bean id="mydate" class="java.util.Date"></bean>
</beans>

spring配置文件,每一个<bean>代表一个对象,默认为单例模式;属性值id表示需要创建的对象对应的类(不能是接口),scope表示创建的对象是单例还是多例;prototype表示多例

- 编写测试用例(这里使用的maven创建的项目)

    @Test
    public void Test02() {
        String config = "applicationContext.xml";
        // ClassPathXmlApplicationContext表示从类路径中加载配置文件
        // 类路径表示从target/class下面寻找文件
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        // 通过getBean创建一个对象
        SomeService s = (SomeService)ac.getBean("Test1");
        s.doSome();
        OtherService other = (OtherService) ac.getBean("Test2");
        other.doOther();

    }

ApplicationContext是一个接口,用于加载spring的配置文件;其实现类ClassPathXmlApplicationContext,用于处理配置文件存放在项目的类路径下。

ApplicationContext容器,在容器初始化的时候,一次性创建所有<bean>标签声明的对象,调用无参构造器;占用内存,以后可以直接通过getBean获取对象

2.基于xml的di

        di:依赖注入,当程序在运行过程中,如果需要调用另一个对象进行协作,不需要在代码中创建被调用者,依赖于外部容器,由外部容器创建传递给程序

        实现对象创建后的属性赋值。分为set注入和构造注入

1)使用set()注入

- .set注入:也成为设值注入,进行赋值的类需要由set()方法,通过set方法由容器完成参数的赋值;传递的参数可以是简单类型或引用类型

applicationContext.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="student" class="com.righteye.ba01.Student">
    <property name="name" value="张三"></property>
    <property name="age" value="18"></property>
    <property name="school" ref="MySchool"></property>
</bean>

<bean id="MySchool" class="com.righteye.ba01.School">
    <property name="name" value="北京大学"></property>
    <property name="address" value="北京"></property>
</bean>

</beans>

        <property>标签,为对象进行赋值。属性name表示类中的属性名;value表示类的属性值,由容器完成注入;

        简单类型(基本数据类型+String)将value的值通过相应的set方法完成注入

        引用类型:如school, 赋值的使用不使用value, 使用ref;

                语法<bean id="xxx", ref="yyy的id">

Student.java

package com.righteye.ba01;

public class Student {

    private String name;
    private int age;

    private School school;

    public Student() {
    }

    public void setName(String name) {
        System.out.println("setName方法被调用");
        this.name = name;
    }

    public void setAge(int age) {
        System.out.println("setAge方法被调用");
        this.age = age;
    }

    public void setSchool(School school) {
        this.school = school;
    }

}

- 构造注入(理解):有参构造函数的属性注入

<bean id="MyStudent1" class="com.righteye.ba02.Student">
       <constructor-arg name="age" value="18"></constructor-arg>
       <constructor-arg name="name" value="zs"></constructor-arg>
   </bean>

<bean> 子标签: <constructor-arg> 构造函数参数

2)引用类型的自动注入

        对于引用类型,可以不再配置文件中显示的注入。通过autowire属性隐式完成,分为两种:byName和byType

        在进行属性赋值的时候,也可以直接使用内部bean,类似匿名内部类,该对象仅对调用者可见

<!--使用内部bean-->
    <bean id="user5" class="com.righteye.spring.day01.User">
        <property name="username" value="乔巴"></property>
        <property name="age" value="18"></property>
        <property name="school">
            <bean class="com.righteye.spring.day01.School">
                <property name="name" value="第五幼儿园ben"></property>
                <property name="address" value="nova4游戏商城"></property>
            </bean>
        </property>
    </bean>
  • byName:按名称自动注入
    • 配置文件中被调用者的<bean> id值与代码中调用者的属性名相同;由容器自动完成属性注入
<?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="student" class="com.righteye.ba01.Student" autowire="byName">
    <property name="name" value="张三"></property>
    <property name="age" value="18"></property>
</bean>

<bean id="school" class="com.righteye.ba01.School">
    <property name="name" value="北京大学"></property>
    <property name="address" value="北京"></property>
</bean>
</beans>

        首先在调用者添加autowire属性,设置为byName, 调用者的School属性名为school,被调用者的id为school,属性名称相同,可以完成属性的自动注入。

  • byType:按类自动注入
    • 配置文件中被调用者bean的class属性指定的类与调用者bean的某引用属性类型同源
    • 同源:相同类;具有继承关系;接口和实现类的关系
    • 同源关系只能同时存在一种
<bean id="MyStudent" class="com.righteye.ba03.Student" autowire="byType">
        <property name="age" value="18"></property>
        <property name="name" value="王五"></property>
    </bean>

    <!--byType的自动注入:相同类-->
    <!--<bean id="MySchool" class="com.righteye.ba03.School">-->
        <!--<property name="name" value="北京大学"></property>-->
        <!--<property name="address" value="北京"></property>-->
    <!--</bean>-->

    <!--byType的自动注入:具有继承关系-->
    <bean id="MyPrimarySchool" class="com.righteye.ba03.PrimarySchool">
        <property name="name" value="人民大学"></property>
        <property name="address" value="北京"></property>
    </bean>

3) <value>属性标签

        在为属性进行赋值的时候,除了使用property中的value属性,也可以使用<value>标签 + 字面量的形式进行赋值;

        在进行属性赋值的时候,可能会有特殊字符的存在,可以使用关键字<![CDATA[]]>

<!--当属性值中有特殊字符的时候可以添加关键字-->
    <!--属性赋值可以使用<value></value>标签-->
    <bean id="user2" class="com.righteye.spring.day01.User">
        <property name="username">
            <value><![CDATA[<<王五>>]]></value>
        </property>
        <property name="age" value="18"></property>
    </bean>

 3.使用p标签

        p标签的使用可以加快开发效率,用来代替<property />

 <!--使用p标签,简化配置文件-->
    <bean id="user" class="com.righteye.spring.day01.User"
          p:username="ww"
          p:age="20"></bean>

    <bean id="teacher" class="com.righteye.spring.day01.Teacher"
          p:name="娜美" p:age="18"
          p:uList-ref="user"
          p:uSet-ref="user">
    </bean>

在IDEA使用p标签后,会在配置文件上面自动导入命名空间:

xmlns:p="http://www.springframework.org/schema/p"

4.基于注解的di

        使用注解,不需要在spring的配置文件中声明bean实例。需要在spring配置文件中声明组件扫描器,对指定的基本包扫描注解

<--扫描base-package路径下的所有类-->
<context:component-scan base-package="xx包名"/>


常用注解:

  • Component:用于创建不清楚具体功能的对象;添加在需要创建对象类的上面

    • @Component(value=id) : 这里的id相当于<bean>中的id,具有唯一性

    • 当Component中不指定value的时候,默认id为类名首字母小写

  • Respority:创建持久层对象,是对@Component的细化

  • Service:创建业务逻辑层对象

  • Controller:创建控制层对象

  • Value:简单类型属性的注入,在需要赋值的属性上面添加@Value(属性值);完成自动注入,属性不需要set方法

   @Value(value="张三")
   private String name;
  • Autowired:默认byType的自动注入(对于引用类型);在引用类型属性上面添加@Autowired注解;含有一个required属性,默认为true, 当属性匹配错误的时候系统运行停止;为false,表示忽略,没有赋值的内容为null

  • Quality:修改引用类型属性自动注入的方式为byName, @Quality(value="id")

// 用于完成自动注入,默认byType
    @Autowired(required = false)
    @Qualifier("mySchool")
    private School school;
  • Resource:由JDK提供的注解,也是为了实现自动注入,当不提供参数的时候,默认方式为byName,如果没有匹配成功,采用类型匹配
    // @Resource注解若不带任何参数,采用默认按名称的方式注入
    // 按名称不能注入bean,则会按照类型进行Bean的匹配注入。 
    @Resource
    private School school;



    //@Component    未指明id,默认为类名首字母小写 school == school, 匹配成功
    // id = MySchool != school, 所以再看类型是否匹配
    @Component("MySchool")
    public class School {
        @Value("清华大学")
        private String name;
        @Value("北京")
        private String address;

    }

5.补充内容

        1).<bean>的继承

        <bean>中包含一个属性parent, 可以继承其他 bean对象的属性;可以定义一个统一的bean的作为模板,由其他bean继承属性,相同的属性进行赋值后者会覆盖前者

<!--定义了User对象的模板-->
    <bean id="user" class="com.righteye.spring.day01.User"
          p:age="18" p:username="ww"
          abstract="true"></bean>

    <bean id="user_son" parent="user"></bean>

    <bean id="teacher" class="com.righteye.spring.day01.Teacher"
          p:name="索隆"
          p:age="19" autowire="byType"></bean>

        其中:parent = “对象id”, 用来继承id; 在模板bean中可以添加abstract, 表示这个对象不会被创建,只能由其他bean继承

        2)使用${}从配置文件读取数据

        在xml文件中添加 <context:property-placeholder>, 参数location的值为配置文件路径位置

这里以数据库配置文件为例: 

<context:property-placeholder location="classpath:demo06/db.properties"></context:property-placeholder>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${db.driver}"></property>
        <property name="url" value="${db.url}"></property>
        <property name="username" value="${db.user}"></property>
        <property name="password" value="${db.password}"></property>
    </bean>

        3)bean对象的生命周期

        <bean>标签中还包括init-method和destroy-method属性,属性值为需要执行的函数名;用来提供对象创建后的初始化操作和销毁前进行的操作(如释放内存)

<bean id="user" class="com.righteye.spring.day01.User"
          p:age="18" p:username="索隆"
          init-method="init"
          destroy-method="close"></bean>

        4)后置处理器

        后置处理器允许在bean初始化前后对bean对象进行额外的操作

        后置处理器会对所有容器对象都会执行相应的操作

        后置处理器需要一个实现了BeanPostProcessor接口的java类,然后实现相应的方法去处理特定的时间点

public class PostHandler implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        System.out.println("BeforeInitialization:" + bean + " " + beanName);


        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        System.out.println("BeforeInitialization:" + bean + " " + beanName);

        return bean;
    }
}

 在spring配置文件中创建后置处理器对象      

<bean class="com.righteye.spring.day01.handler.PostHandler"></bean>

        5)使用静态工厂创建bean对象

public class StaticUserFactory {

    private static User user;

    public static User getUser(Integer age, String username) {
        user = new User();
        user.setAge(age);
        user.setUsername(username);
        return user;
    }

}

     在bean对象中导入静态工厂类以及使用factory-method调用的方法, 通过构造器对属性进行赋值

<bean id="static_user" class="com.righteye.spring.day01.factory.StaticUserFactory"
          factory-method="getUser">
        <constructor-arg name="username" value="山治"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
    </bean>

   

三、面向切面编程(AOP)

        动态代理:通过动态生成代理对象,有代理对象创建目标类;并对目标类的业务功能进行增强

jdk的动态代理:是通过接口完成动态代理,主要类为InnovationHandler, Method, Proxy进行操纵

cglib的动态代理:不需要接口,通过继承的方式对目标类进行增强

动态代理的优势

        使目标类专心业务逻辑;

        提供代码复用率

        解耦合,让业务功能和非业务功能分离

1)什么是aop?

        面向切面编程;基于动态代理,是动态代理的一种规范化,把动态代理的实现步骤进行了统一(统一jdk动态代理和cglib代理情况下的差异),方面进行编码

2)aop的术语

Aspect: 切面, 给目标类增加的功能,就是切面。例如日志,事务都是切面

切面的特点:一般都是非业务方法,独立使用的

JoinPoint:连接点,连接业务方法和切面的位置。

Pointcut:切入点,指连接点方法的集合

目标对象:给哪个类添加切面

Advice:通知,通知切面的执行时间

3)aop实现

        spring框架中提供了aop规范,主要使用在事务处理,但一般不使用,比较笨重;

        aspectJ, 由ecplise基金会提供的,开源专门做aop框架。spring中继承了aspectJ框架,可以使用xml配置文件/使用注解(5个:Before, AfterReturing, Around, After, AfterThrowing

使用aspectJ实现aop的基本步骤:

  • 建立maven项目
  • 添加依赖
    • spring依赖
    • aspectJ的依赖
    • junit单元测试
  • 创建目标类:接口和他的实现类
  • 创建切面类
    • 在类的上面使用@aspect
    • 在类中定义方法,即切面要执行的功能代码;在方法上面加入aspectJ中的注解,指定切入点表达式
  • 创建配置文件
    • 声明目标类和切面类
    • 声明aspectJ框架中的自动代理生成器标签,用来完成代理对象的创建。
<!--添加ajpectJ依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

- 切入点表达式:

        execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)  throws-pattern?) 

modifiers-pattern 访问权限类型

ret-type-pattern 返回值类型

declaring-type-pattern 包名类名

name-pattern(param-pattern) 方法名(参数类型和参数个数)

throws-pattern 抛出异常类型  ?表示可选的部分 

execution(访问权限 方法返回值 方法声明(参数) 异常类型) 

- 配置文件 applicationContext.xml

        自动代理生成器原理:自动代理生成器会寻找所有添加@Aspect的类,然后再由切面类通过切面点找到需要增强方法的位置,并通过注解找到添加的时间

    <!--创建目标类-->
    <bean id="myTarget" class="com.righteye.ba02.SomeServiceImpl"></bean>

    <!--创建切面类-->
    <bean id="myAspect" class="com.righteye.ba02.MyAop"></bean>

    <!--创建自动代理生成器-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

- 注解@Before

        前置通知,包含一个JoinPoint参数,可以获取切入点表达式,方法签名,目标对象等

要求:方法public, 没有返回值,可以有参数

    /**
     *  前置通知:方法公有
     *  没有返回值,方法名称自定义,参数可以有或没有
     */
    @Before(value="execution(public * com.righteye.ba02.SomeServiceImpl.doSome(..))")
    public void doLog() {
        System.out.println("非业务方法:" + new Date());
    }

- 注解@AfterReturning

    /**
     * @AfterReturning
     *  后置通知
     *      方法公有,没有返回值,有参数,方法名称自定义
     *
     *  注解参数:value :切入表达式
     *      returning: 用来接收目标方法的返回值,必须和通知方法的参数名相同
     *      对于目标方法中获取的参数,如果是String, 因为字符串是不可变的所以修改结果没有改变
     *      如果是引用类型,因为引用传递的原因可以修改
     */
    @AfterReturning(value="execution(public * com.righteye.ba02.SomeServiceImpl.doOther(..))"
            , returning="obj")
    public void doThing(Object obj) {
        System.out.println("后置通知:提交事务");
        obj += "我被修改";
        System.out.println("修改后的obj参数:" + obj);
    }

- 注解@AfterReturning

    /**
     * @Around 环绕通知
     *  方法公有, 有返回值,自定义方法名称,有参数:ProceddiingJoinPoint
     *
     * @Around 类似JDK的动态代理,而参数ProceedingJoinPoint类似Method,能够调用目标类方法
     *      该注解常常使用在事务的提交方面;能够控制目标类方法是否能够正常被调用
     */
    @Around(value="execution(* com.righteye.ba02.SomeServiceImpl.doAround(..))")
    public void doAroundThing(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("Around通知前置任务:" + new Date());

        pjp.proceed(); // 控制目标方法是否被调用

        System.out.println("Around通知后置任务:提交事务");
    }

上述使用aop时候切入点表达式需要写在代码中方法的上面,耦合性过高

通过xml配置aop的切入点表达式:具体配置在这篇文章

https://blog.csdn.net/qq_45888932/article/details/122543155

四、事务

1.什么是事务?

事务是一组sql语句的集合,希望这一组sql语句的执行是一致的,要么同时发生,要么都不发生

2.在java代码中的dao层时,会调用一组sql语句操作数据库,所以在dao层的时候应该使用事务

由于可能使用多种不同的工具访问数据库,如mysql, mybatis, 其他数据库访问技术,造成在事务处理的时候方法不同,为了解决这个麻烦,spring统一,抽象出处理步骤,屏蔽了具体实现步骤

声明式事务:把事务相关的资源和内容提交给spring,由spring完成事务的处理

3.spring处理事务的步骤:

  • 使用事务管理对象,是一个接口和他众多实现类的集合。代替完成事务的处理

接口:PlatformTransactionManger,定义了事务的commit和roolback

实现类:将访问数据的库的不同技术都做了相应的封装

在配置文件中使用<bean id="xxx" class="…..“/> 定义相应的访问数据库的方法

  • 事务的类型
    • 事务的隔离级别:

READ_UNCOMMITTED:读未提交,没解决任何并发问题

READ-COMMITTED:读已提交。解决脏读,存在不可重复读和幻读

REPEATABLE_READ: 可重复读。存在幻读问题

SERIALIZABLE:串行化

  • 事务的超时时间:表示一个方法最长执行时间
  • 事务的传播行为:控制业务方法是否有事务,是什么种类的事务
    • PROPAGATION_REQUIRED
    • PROPAGATION_REQUIRES_NEW
    • PROPAGATION_SUPPORTS
  • 事务提交的时机:
    • 当业务方法没有抛出异常的时候,提交事务
    • 当抛出运行时异常,回滚事务
    • 如果是ERROR或者编译时异常,提交事务

 - 事务处理的解决方案

  • 适用于中小型项目,注解方案

使用@Transaction增加事务

1.在配置文件中声明事务管理器<bean …/>

2.开启spring事务注解驱动。spring使用aop机制,对添加@Transactional注解所在类创建代理方法,给方法添加事务功能;这里的注解驱动要选择类名后缀tx

使用的是aop的环绕通知

<tx:annotation-driven transaction-manager="事务管理器id">

3.给方法添加@Transactional注解

在需要添加事务的业务方法上面添加注解:@Transactional

  • 声明式事务处理

使用aspectJ提供的aop机制创建代理对象完成事务操作

第一种方法的缺点:使用XML配置事务代理的方式,每个目标类都需要配置事务代理。当目标类较多,配置文件会变得非常臃肿。 在每个方法的上面都要添加注解。

java代码和xml的配置文件没有完全分离

步骤:

1.添加aspectJ的依赖

2.配置事务管理器

3.配置事务通知。用于指定将事务以什么方式加入到哪些方法

4.配置增强器。使用aop机制,给目标类创建代理对象

声明式事务处理使用的aspectJ中的aop机制,所以有容器声明的最终对象式通过代理模式产生的对象

总结

用来做笔记而已。试试博客啥样

刚开始学,很多漏洞,望更正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值