spring框架基础快速入门

spring

概念

spring是一个项目管理框架,同时也是java EE解决方案
spring是众多优秀设计模式的组合(工厂,单列,代理,适配器,包装器,观察者,模板,则略)
spring并未代替现有的框架产品,而是将众多框架进行有机

spring框架的组成

spring框架由诸多模块组成,可分类为
核心技术:依赖注入,事件,资源,i18n,验证,数据绑定,类型转换,spEL,AOP
测试:模拟对象,TestContext框架,springMVC,webTestClient
数据访问:事务,DAO支持,JDBC,ORM
Spring MVC 和spring WebFlux web框架
继承:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。
语言:kotlin,GROOVY,动态语言

在这里插入图片描述

spring环境搭建

依赖

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.7.RELEASE</version>
</dependency>

创建spring配置文件

初步测试

spring-context.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">


</beans>

完整版

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

</beans>

srping工厂编码

定义Bean类型

package com.blb.dao;

public class UserDaoImpl implements UserDao{

    public void deleteUser(Integer id) {
        System.out.println("删除用户");
    }
}

package com.blb.service;

public class UserServiceImpl implements UserService{
    public void deleteUser(Integer id) {
        System.out.println("删除用户");
    }
}

spring-context.xml中的< beans >内配置bean标签

配置实例 (id ="唯一标识" class="需要被创建的目标对象全限定名"
<?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="userDao" class="com.blb.dao.UserDaoImpl"></bean>
    <bean id="userService" class="com.blb.service.UserServiceImpl"></bean>
</beans>

调用spring工厂的API(ApplicationContext接口)

  //读取配置文件中所需要创建的bean对象,并获得工厂对象
ApplicationContext context=new ClassPathXmlApplicationContext("/spring-context.xml");
       //通过id获取bean对象
        UserDao userDao=(UserDao)context.getBean("userDao");
        UserService userService = (UserService)context.getBean("userService");
         
        userDao.deleteUser(1);
        userService.deleteUser(1);

依赖与配置文件

spring框架包含多个模块,每个模块各司其职,可结合需求引入相关依赖jar包实现功能

spring依赖关系

在这里插入图片描述

IOC(inversion of Control) 控制反转

inverse Of controll :控制反转
反转了依赖关系的满足方式,由之前的自己创建依赖对象,变为由工厂推送(主动变为被动,即反转)解决了具有依赖关系的组件之间的强耦合,使得项目型态更加稳健

ioc优点

解耦,是降低程序耦合度,也就是减少程序代码之间的依赖性,如果代码之间的依赖性很高,修改一处代码会影响很多其他的代码,这就给项目的稳定性带来的问题,不利于代码的扩展和维护。

没有IOC的程序中,我们使用new来完成对象的创建,如果需要的对象的类型发生改变,就需要手动修改代码。

有了IOC后,对象的创建由第三方(Spring容器)完成,由Spring来管理应用中所有对象的生命周期,开发者只需要关注自己的业务逻辑,代码更利于扩展和维护

项目中的强耦合问题

package com.blb.service;

import com.blb.dao.UserDao;
import com.blb.dao.UserDaoImpl;

public class UserServiceImpl implements UserService{
    //满足依赖关系强耦合
    
    //强耦合了UserDaoImpl,使得UserServiceImpl变的不稳健了
    private UserDao userDao=new UserDaoImpl();

   
    public void deleteUser(Integer id) {
        userDao.deleteUser(1);
    }

   
}

解决方案

package com.blb.service;

import com.blb.dao.UserDao;
import com.blb.dao.UserDaoImpl;

public class UserServiceImpl implements UserService{
    //满足依赖关系强耦合


    //不引用任何一个具体的主键(实现类)在需要其他主键的位置预留存取值入口
    private UserDao userDao;
    //给userDao定义get和set方法允许userDao接收spring赋值
    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    
    
    public void deleteUser(Integer id) {
        userDao.deleteUser(1);
    }

  
}
<!--       要生产的对象-->
    <bean id="userDao" class="com.blb.dao.UserDaoImpl"></bean>
    <bean id="userService" class="com.blb.service.UserServiceImpl">
<!--        由spring为userDao属性赋值,ref是id为userDao的一个bean-->
        <property name="userDao" ref="userDao"></property>
    </bean>

此时,如果需要更换其他UserDAO,则UserServiceImpl不用任何改动,只需要换一下里面的配置,则此时的UserServicImpl组件变得更加稳健

DI(dependency injection)依赖注入

概念

在spring创建对象的同时,为其属性赋值,称之为依赖注入
  • 依赖注入(DI): Dependency Injection

  • 把有依赖关系的类放到容器中,解析出这些类的实例,就是依赖注入。

  • 依赖注入是实现IOC的一种方式。

  • IoC可以认为是一种全新的设计模式,但是理论和时间成熟相对较晚,并没有包含在GoF(23种设计模式)中

< bean>元素的常用属性及其子元素

id

bean的唯一标识,同一个Spring容器中不允许重复

class

全类名,用于反射创建对象

scope

​ scope主要有两个值:singleton和prototype

​ 如果设置为singleton则一个容器中只会有这个一个bean对象。默认容器创建的时候就会创建该对象。

​ 如果设置为prototype则一个容器中会有多个该bean对象。每次调用getBean方法获取时都会创建一个新对象

属性或子元素名称描述
idBean在BeanFactory中的唯一标识,在代码中通过BeanFactory获取Bean实例时需要以此作为索引名称
classBean的具体实现,使用类的完整限定类名
scope制定Bean实例的作用域
< constructor-arg >子元素,使用构造方法注入,指定构造方法的参数,该元素的index属性指定参数的序号,value属性指定Bean的属性值,ref属性指定对 BeanFactory中的其他Bean的引用关系,type属性指定参数类型,value指定参数的常量值
< list >< property > 元素的子元素,用于封装List或者数组类型依赖注入
< map >< property > 元素的子元素,用于封装Map类型依赖注入
< SET >< property > 元素的子元素,用于封装Set类型依赖注入
< entry >< map > 元素的子元素,用于设置一个键值对

set注入

默认是通过set方法来实现的所以如果发现提示报红,注意检查下是否添加了set方法

public class User {
    private Integer id;
    private String password;
    private String sex;
    private Integer age;
    private Date bornDate;
    private String[] hobbys;
    private List<String> names;
    private Set<String> phones;
    private Map<String,String> countries;
    private Properties files;
    private address address;

Getters and Setters
}

基本类型+字符串类型+日期类型

 <!--
            name属性用来指定要设置哪个属性
            value属性用来设置要设置的值
            ref属性用来给引用类型的属性设置值,可以写上Spring容器中bean的id
        -->
 <bean id="user" class="com.blb.entity.User">
<!--        简单基本类型 包括string 和 Date-->
        <property name="id" value="1"></property>
        <property name="password" value="123456"></property>
        <property name="sex" value="男"></property>
        <property name="age" value="18"></property>
        <property name="bornDate" value="2021/3/28 12:00:00"></property>
</bean>       
        

注意日期的格式是用"/"来分隔,spring会自动转化为日期类型

集合类型

数组
<!--        数组-->
        <property name="hobbys">
             <array>
                 <value>football</value>
                 <value>basketball</value>
             </array>
        </property>
list
<!--        list-->
        <property name="names">
            <list>
                <value>远科</value>
                <value>小科</value>
            </list>

        </property>
set
<!--      set  -->
        <property name="phones">
            <set>
                <value>110</value>
                <value>120</value>
            </set>
        </property>
map
<!--       map -->
        <property name="countries">
            <map>
                <entry key="zh" value="china"></entry>
                <entry key="en" value="english"></entry>
            </map>
        </property>
Properties
<property name="files" >
            <props>
                <prop key="url">jdbc:mysql:xxx</prop>
                <prop key="username">root</prop>
            </props>
 </property>
空指针null注入
<!--空指针null注入 -->
		<property name="xxx">
			<null></null>
		</property>

自建类型

public class User {
    private address address;
}
//次要bean,被作为属性
 <bean id="addr" class="com.blb.entity.address">
        <property name="city" value="wh"></property>
        <property name="id" value="1"></property>
 </bean>

<!--ref引用spring中已经创建很好的对象-->
<!--value是一个具体的值,基本数据类型-->

//主要bean,操作的主体
<bean id="user" class="com.blb.entity.User">
  <property name="address" ref="addr"></property>
    </bean>

构造注入

创建对象时,spring工厂会通过构造方法为对象的属性赋值

定义目标Bean类型

package com.blb.entity;

public class student {
    private int id;
    private String name;
    private String sex;
    private Integer age;

    public student(int id, String name, String sex, Integer age) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

    @Override
    public String toString() {
        return "student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                '}';
    }
}

注入


<!--    构造注入-->

<!-- 参数名是name,则有name="构造方法的形参名" -->
   <bean id="student" class="com.blb.entity.student">
       <constructor-arg name="age" value="18"></constructor-arg>
       <constructor-arg name="id" value="1"></constructor-arg>
       <constructor-arg name="sex" value="male"></constructor-arg>
       <constructor-arg name="name" value="dyk"></constructor-arg>
   </bean>

<!--    构造注入-->

<!--根据参数顺序注入 -->
   <bean id="student" class="com.blb.entity.student">
       <constructor-arg index="0" value="18"></constructor-arg>
       <constructor-arg index="1" value="1"></constructor-arg>
       <constructor-arg index="2" value="male"></constructor-arg>
       <constructor-arg index="3" value="dyk"></constructor-arg>
   </bean>

自动注入

不用在配置中,指定为那个属性赋值,及赋什么值
由spring自动根据某个原则,在工厂中找一个bean,为属性注入属性值

1. byType自动装配:byType会自动查找,和自己对象set方法参数的类型相同的bean

   保证所有的class唯一(类为全局唯一)

2. byName自动装配:byName会自动查找,和自己对象set对应的值对应的id

   保证所有id唯一,并且和set注入的值一致
<!--       要生产的对象-->
    <bean id="userDao" class="com.blb.dao.UserDaoImpl"></bean>
    <!-UserServiceImpl中的属性基于属性名称自动注入-->
    <bean id="userService" class="com.blb.service.UserServiceImpl" autowire="byName">

    </bean>
<!--       要生产的对象-->
    <bean id="userDao" class="com.blb.dao.UserDaoImpl"></bean>
    <!-UserServiceImpl中的属性基于类型自动注入-->
    <bean id="userService" class="com.blb.service.UserServiceImpl" autowire="byType">

    </bean>

如果以属性名自动填充那么就要id和属性名一样,如果以属性类型自动填充,那么同一个类型不能由多个

小结:

  • byName的时候需保证需要注入的bean的id唯一,并且需要匹配的id值必须跟需要注入的属性名一致。
  • byType的时候需保证需要注入的bean的class唯一,并且这个bean的类型跟需要注入的属性类型一致。
  • 所有bean的autowire属性默认为no,但是可以在标签下使用default-autowire="XX"来修改整个配置文件的默认自动装备的值。

拓展注入

使用p和c命名空间需要导入xml约束
xmlns:p=“http://www.springframework.org/schema/p”
xmlns:c=“http://www.springframework.org/schema/c”

?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--p命名空间注入/set注入,可以直接注入属性的值-》property-->
    <bean id="user" class="pojo.User" p:name="cxk" p:id="20" >
    </bean>

    <!--c命名空间,通过构造器注入,需要写入有参和无参构造方法-》construct-args-->
    <bean id="user2" class="pojo.User" c:name="cbh" c:id="22"></bean>
</beans>
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

Student student = context.getBean("user",User.class);
System.out.println(student);

总结:

p命名名

  • 需要引入P的命名空间
  • 是通过set方式注入的,需要确保有setter方法

c命名

  • 需要导入C命名空间
  • 是通过构造器方式注入,需要确保有对应合法参数列表的构造器

Bean的一些细节

Bean 的作用域

作用域描述
singleton默认的作用域,使用singleton定义的Bean在spring容器中只有一个Bean实例
prototypespring 容器每次获取prototype定义的Bean,容器都将创建一个新的Bean实例
request在一次HTTP请求中容器将返回一个Bean实例,不同的HTTP请求返回不同的Bean实例,仅在Web Spring应用程序的上下文中使用
session在以此HTTP Session中,容器将返回同一个Bean实例,仅在Web Spring应用程序的上下文中使用
application为每个ServletContext对象创建一个实例,即同一个应用共享一个Bean实例,仅在Web Spring应用程序的上下文中使用
websocket为每个WebSocket对象创建一个Bean实例 仅在Web Spring应用程序的上下文中使用

控制简单对象的单列,多例模式

配置<bean scope="singleton|prototype">

singleton(默认) :每次调用工厂,得到的都是同一个对象
prototype: 每次调用工厂,都会创建新对象

<bean id="userDao" class="com.blb.dao.UserDaoImpl" scope="singleton"></bean>

注意:根据场景决定对象的单列,多例模式
可以共用:Service DAO sqlSessionFactory(或者是所有的工厂)
不可共用:Connection SqlSession

User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
system.out.println(user == user2)//结果为true

总结:在配置文件加载的时候,容器(< bean>)中管理的对象就已经初始化了

FactoryBean创建复杂对象

让spring可以创建复杂对象,或者无法直接通过反射创建的对象
例如:ConnectionSqlSessionFactory

在这里插入图片描述

实现FactoryBean接口

package com.blb.factorybean;

import org.springframework.beans.factory.FactoryBean;

import java.sql.Connection;
import java.sql.DriverManager;

public class ConnectionFactoryBean implements FactoryBean<Connection> {
    @Override
    public Connection getObject() throws Exception {
        Class.forName("com.mysql.cj.jdbc.Driver");
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/db3?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8","root","123456");

    }

    @Override
    public Class<?> getObjectType() {
        return Connection.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}

注意 isSingleton方法的返回值,需要根据所创建对象的特点决定返回true/false
例如Connection不应该被多个用户共享,返回false
例如SqlSessionFactory重量级资源,不该多创建,返回true

配置与获取

<!--    复杂对象-->
<!--当从工厂对象索要一个bean时,如果是factorybean 实际返回的是工厂Bean的getobject方法的返回值-->
    <bean id="conn" class="com.blb.factorybean.ConnectionFactoryBean"></bean>
 ApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
        Connection conn = (Connection)context.getBean("conn");
         PreparedStatement pst = conn.prepareStatement("select * from  t_user");
         ResultSet rs = pst.executeQuery();
         while(rs.next()){
             System.out.println(rs.getInt("id"));
         }

在这里插入图片描述

Spring配置

别名

<bean id="user" class="pojo.User">
    <constructor-arg name="name" value="chen"></constructor-arg>
</bean>

<alias name="user" alias="userLove"/>
<!-- 使用时
	User user2 = (User) context.getBean("userLove");	
-->

Bean的配置

<!--id:bean的唯一标识符,也就是相当于我们学的对象名
class:bean对象所对应的会限定名:包名+类型
name:也是别名,而且name可以同时取多个别名 多个别名之间可以用空格、逗号、封号隔开 -->
<bean id="user" class="pojo.User" name="u1 u2,u3;u4">
    <property name="name" value="chen"/>
</bean>
<!-- 使用时
	User user2 = (User) context.getBean("u1");	
-->
  • d:bean的唯一标识符,相当于我们取的对象名
  • class:bean对应的全限定名: 包名+类名
  • name:也是别名,而且可以取多个别名,多个别名之间可以用空格、逗号、封号隔开

import

import一般用于团队开发使用,它可以将多个配置文件,导入合并为一个

<import resource="beans.xm1"/>
<import resource="beans2.xml"/>
<import resource="beans3.xm1"/>

使用的时候,直接使用总的配置就可以了
按照在总的xml中的导入顺序来进行创建,后导入的会重写先导入的,最终实例化的对象会是后导入xml中的那个

当有冲突的时候按照顺序后面覆盖前面的原则

spring工厂特性

饿汉式创建优势

工厂创建之后,会将spring配置文件的所有对象都创建完成(饿汉式)
提高程序运行效率,避免多次IO,减少对象创建时间(概念接近连接池,一次性创建好,使用时直接获取)

生命周期

自定义初始化方法:在bean里添加init-method属性=方法名 spring会在创建对象之后,调用次方法
自定义销毁方法 ,在bean里添加destroy-method属性=方法名 spring会在销毁对象之前调用次方法
销毁:工厂的close()方法被调用之后,spring会销毁所有已创建的单利对象
分类:Singleton对象由spring容器销毁,prototype对象由JVM销毁

package com.blb.entity;

public class address {
    private Integer id;
    private String city;

    public address() {
        System.out.println("构造方法");
    }

    public Integer getId() {

        return id;
    }

    public void setId(Integer id) {
        System.out.println("set方法");
        this.id = id;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public void init(){
        System.out.println("初始化");
    }
    public void destory()
    {
        System.out.println("销毁");
    }
    @Override
    public String toString() {
        return "address{" +
                "id=" + id +
                ", city='" + city + '\'' +
                '}';
    }
}

<bean id="addr" class="com.blb.entity.address" init-method="init" destroy-method="destory">
        <property name="city" value="wh"></property>
        <property name="id" value="1"></property>
     </bean>

或者使用注解

@PostConstruct  //初始化
    public void init(){
        System.out.println("初始化");
    }
    @PreDestroy
    public void destory()
    {
        System.out.println("销毁");
    }

声明周期阶段

单利bean:singleton

单例模式下容器只会创建一个实例,每次从容器中获取的都是同一个对象


随工厂启动:创建》构造方法》set方法(注入值)》init(初始化)》构建完成》随工厂关闭销毁

多例bean:prototype

原型模式下每次从容器中获取的都是一个新的对象


被使用时创建》构造方法》set方法(注入值)》init(初始化)》构建完成》JVM垃圾回收销毁

代理设计模式

概念

将核心功能与辅助功能(事务,日志,性能监控代码)分离,达到核心业务功能更纯粹,辅助业务功能服用

在这里插入图片描述

静态代理设计模式

通过代理类的对象,为原始类的对象(目标类的对象)添加辅助功能,更容易替换实现类,利于维护。

在这里插入图片描述

package com.blb.service;

public interface FangDongService {
public  void zufang();


}
package com.blb.service;

public class FangDongServiceImpl implements FangDongService{

    @Override
    public void zufang() {
        //    核心功能
        System.out.println("签合同");
        System.out.println("租房");
    }
}

package com.blb.service;

public class FangDongProxy implements FangDongService{

 private FangDongService fangDongService=new FangDongServiceImpl();
    @Override
    public void zufang() {
        //辅助功能,额外功能
        System.out.println("发布租房信息");
        System.out.println("带租客看房");

//        核心=原始业务类
        fangDongService.zufang();
        System.out.println("售后");
    }
}

代理类=实现原始类相同接口+添加辅助功能+调用原始类的业务方法

静态代理的问题

代理数量过多不利于项目的管理
多个代理类的辅助功能代码冗余,修改时,维护性差

总结:

  • 代理间接执行核心业务,扩展非核心业务。
  • 代理模式很好的将核心业务与公共的非核心业务隔离开,方便集中管理。
  • 缺点是一个代理角色只代理一个真实角色。

动态代理设计模式

为了解决静态代理中一个代理只代理一个真实角色的问题,我们可以使用动态代理来解决。动态代理中的代理类是动态生成的,不是直接写好的。底层是通过反射实现的

动态代理分为2大类:

  • 基于接口的动态代理:

    • JDK的动态代理
  • 基于类的动态代理:

    • cglib
    • java字节码:javasist(jboss)
动态创建代理对象,为原始类的对象添加辅助功能

JDK动态代理实现(基于接口)

在这里插入图片描述

  • 代理者的角色已经没有了,而是由Proxy动态生成。
  • 生成的代理者需要实现抽象角色的核心业务,这样才知道要代理什么核心业务
  • 调用代理者的代理业务时不直接执行,而且间接通过InvocationHandler中方法invoke反射执行
  • InvocationHandler的作用理解为监控并执行代理者的核心业务方法的调用,监控意味着可以在核心业务中增强非核心的业务。
  • Proxy是JDK中的类,InvocationHandler是JDK中的接口
//目标
        FangDongService fangDongService=new FangDongServiceImpl();

        //额外功能 设置回调函数
        InvocationHandler ih=new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //辅助功能,额外功能
                System.out.println("发布租房信息");
                System.out.println("带租客看房");

              // 核心=原始业务类
                fangDongService.zufang();

                return null;
            }
        };
        //动态生成 代理类
        FangDongService proxy= (FangDongService)Proxy.newProxyInstance(test.class.getClassLoader(), fangDongService.getClass().getInterfaces(), ih);

        proxy.zufang();

详细版

定义核心业务
public interface Business {
	
//	核心业务:房屋买卖
	public void buySellHouse() ;

}
定义被代理者
public class HouseOwner  implements Business {
// 业务才能进行核心业务
	@Override
	public void buySellHouse() {
		System.out.println("进行房屋买卖业务....");
	}

}
定义InvocationHandler的实现类
public class ProxyInvocationHandler  implements InvocationHandler{

	private Business   business ;
	
	public void setBusiness(Business business) {
		this.business = business;
	}

	//调用代理类的核心业务方法	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//		扩展非核心业务
		System.out.println("打广告……");
		System.out.println("带人看房……");
//		间接执行被代理者的核心业务
		Object result = method.invoke(business, args);
//		扩展非核心业务
		System.out.println("售后……");
		return result;
	}

}
测试
//		定义真实角色
HouseOwner ho = new HouseOwner();

//		定义InvocationHandler并把代理接口注入进去
ProxyInvocationHandler  handler = new ProxyInvocationHandler();
handler.setBusiness(ho); 
//		生成代理类
Business proxy = (Business) Proxy.newProxyInstance(ho.getClass().getClassLoader(), ho.getClass().getInterfaces(), handler);
//		调用生成的代理类的代理方法
proxy.buySellHouse();
优化
public class ProxyInvocationHandler implements InvocationHandler {
	
	private Object target ;
	
	public Object getProxy(Object target){
		this.target = target;
		return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}
	

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("房屋推广……");
		System.out.println("看房……");
		Object obj = method.invoke(target, args);
		System.out.println("收中介费……"); 
		System.out.println("房屋售后……");
		return obj;
	}

}
测试
HouseOwner ho = new HouseOwner();	 
ProxyInvocationHandler   handler = new ProxyInvocationHandler();
House house = (House) handler.getProxy(ho);		
house.houseBuySell();

CGlib动态代理实现(基于继承)

在这里插入图片描述

  • 虚线的代理者是由cglib中的Enhancer生成的。
  • 代理者为动态生成的,它是被代理者是子类。
  • 代理者继承之后会重写被代理者的方法,然后调用MethodInterceptor中的intercept方法。
导包
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>
 //目标
        FangDongService fangDongService =new FangDongServiceImpl();
       //创建字节码增强对象
        Enhancer enhancer=new Enhancer();
        //设置父类(等价于实现原始类接口)
        enhancer.setSuperclass(FangDongServiceImpl.class);
        enhancer.setCallback(new org.springframework.cglib.proxy.InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {

                //辅助功能,额外功能
                System.out.println("发布租房信息");
                System.out.println("带租客看房");

                // 核心=原始业务类
                fangDongService.zufang();

                return null;
            }


        });
        //            动态生成代理类
        FangDongServiceImpl proxy=(FangDongServiceImpl) enhancer.create();
        proxy.zufang();

完整

定义MethodInterceptor
public class ProxyMethodInterceptor implements MethodInterceptor{

	/**
     * sub:cglib生成的代理对象
     * method:被代理对象方法
     * objects:方法入参
     * methodProxy: 代理方法
     */
	@Override
	public Object intercept(Object obj, Method method, Object[] objects,
			MethodProxy methodProxy) throws Throwable {
//		扩展非核心业务
		System.out.println("打广告……");
		System.out.println("带人看房……");
//		间接执行被代理者的核心业务
		Object o = methodProxy.invokeSuper(obj, objects);
//		扩展非核心业务
		System.out.println("售后……");
		return o;
	}

}
测试
// 代理类class文件存入本地磁盘方便我们反编译查看源码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\");
		
		// 通过CGLIB的Enhancer对象来动态创建代理类
		Enhancer enhancer = new Enhancer() ;
		
		// 这一步就是告诉cglib,生成的子类需要继承哪个类
		enhancer.setSuperclass(HouseOwner.class);
		
		// 设置回调对象,在回调方法中监控并增强代理方法
		enhancer.setCallback(new ProxyMethodInterceptor());
		
		/* 创建代理对象,代理类是被代理的子类,多态
        	第一步、生成源代码
        	第二步、编译成class文件
        	第三步、加载到JVM中,并返回被代理对象 */
		HouseOwner ho = (HouseOwner) enhancer.create();
		
		// 通过代理对象调用目标方法
		ho.buySellHouse();
优化
public class ProxyMethodInterceptor implements MethodInterceptor {

	public Object getProxy(Class clazz) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		return enhancer.create();
	}

	@Override
	public Object intercept(Object obj, Method method, Object[] objects,
			MethodProxy methodProxy) throws Throwable {
		// 扩展非核心业务
		System.out.println("打广告……");
		System.out.println("带人看房……");
		// 反射父类(真实角色)的方法,如果写成invoke会死循环调用
		Object result = methodProxy.invokeSuper(obj, objects);
		// 扩展非核心业务
		System.out.println("售后……");
		return result;
	}

}

总结

ProxyMethodInterceptor interceptor = new ProxyMethodInterceptor() ;
HouseOwner proxy =  (HouseOwner) interceptor.getProxy(HouseOwner.class);
proxy.houseBuySell();
  • JDK动态代理是基于接口,而cglib是基于类。

  • 一般采用JDK方式,因为这种方式效率比较高。

AOP

AOP(Aspect oriented programming)即面向切面编程,即利用一种成为横切的技术,抛开封装的对象内部,并将
那些影响多个类的公共行为封装到一个可重用的模块,将其命名为Aspect 即切面,简单说就是那些与业务无关,却
为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的
可操作性和可维护性
  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…
  • 切面(Aspect):横切关注点 被模块化的特殊对象。即,它是一个类。(Log类)
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。(Log类中的方法)
  • 目标(Target):被通知对象。(生成的代理类)
  • 代理(Proxy):向目标对象应用通知之后创建的对象。(生成的代理类)
  • 切入点(PointCut):切面通知执行的”地点”的定义。(最后两点:在哪个地方执行,比如:method.invoke())
  • 连接点(JointPoint):与切入点匹配的执行点。

作用

soring的AOP编程即是通过动态代理为原始类的方法添加辅助功能

依赖

<!--spring AOP的包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>

spring-context.xml引入AOP命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd 
       http://www.springframework.org/schema/util 
       https://www.springframework.org/schema/util/spring-util.xsd">

</beans>

< aop:config> 元素及其子元素

元素名称用途
< aop:config>在配置文件的beans下可以包含多个该元素
< aop:aspect>配置一个切面,< aop:config>元素的子元素,属性ref指定切面的定义
< aop:pointcut>配置切入点,aop:aspect 元素的子元素,属性expression指定那些
< aop:advisor>定义通知器(通知器跟切面一样,也包括通知和切点)

定义通知类(添加额外功能)

package com.blb.advice;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class BeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        //辅助功能,额外功能
        System.out.println("发布租房信息");
        System.out.println("带租客看房");
    }
}

具体选择前置,后置还是环绕,主要是根据需求来,例如租房,只有在发布信息,带租客看房之后,才能,签合同租房

定义bean标签

<!--    原始对象-->
    <bean id="fangdongservice" class="com.blb.service.FangDongServiceImpl"></bean>
    
<!--    辅助对象-->
    <bean id="beforedvice" class="com.blb.advice.BeforeAdvice"></bean>

定义切入点,形成切面

<!--    定义切入点-->
    <aop:config>
<!--        切入点 [修饰符 返回值 包.类 方法名 参数列表]-->
        <aop:pointcut id="pc_fangdong" expression="execution(* zufang())"/>
        
<!--  组装-->
        <aop:advisor advice-ref="beforedvice" pointcut-ref="pc_fangdong"></aop:advisor>
    </aop:config>
  ApplicationContext context=new ClassPathXmlApplicationContext("/spring-context.xml");
        FangDongService proxy = (FangDongService)context.getBean("fangdongservice");
        System.out.println(proxy.getClass());
        proxy.zufang();

通知类

定义通知类,达到通知效果

前置通知  MethodBeforeAdvice  //在核心之前执行
后置通知  AfterAdvice   //是后置通知和异常通知的父类,核心之后执行
后置通知  AfterReturningAdvice  //有异常不执行,方法会员异常而结束,无返回值
异常通知  ThrowsAdvice //抛出异常才执行
环绕通知	 MethodInterceptor  //核心的前后分别执行

MethodInterceptor

package com.blb.advice;


import org.springframework.aop.MethodBeforeAdvice;


import java.lang.reflect.Method;

public class BeforeAdvice implements MethodBeforeAdvice{
  //method:要执行的目标对象的方法
    //args:参数
    //target:目标对象
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        //辅助功能,额外功能
        System.out.println("发布租房信息");
        System.out.println("带租客看房");
    }
}

AfterReturningAdvice

package com.blb.advice;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class AfterAdvice implements AfterReturningAdvice {
    //后置通知,在核心之后执行,如果核心有异常,则不执行
    //returnVaule: 返回值
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("after入住");
    }
}

ThrowsAdvice

package com.blb.advice;

import org.springframework.aop.ThrowsAdvice;

public class Throwsadvice implements ThrowsAdvice {
    //在核心中抛出异常会执行
    public void afterThrowing(Exception e){
        System.out.println("异常");
    }
}

注意:该消息必须在方法中抛出异常

package com.blb.service;

public class FangDongServiceImpl implements FangDongService{

    @Override
    public void zufang() {
        //    核心功能
        System.out.println("签合同");
        System.out.println("租房");
        if(1==1){
            throw new NullPointerException("空指针");
        }
    }
}

MethodInterceptor

package com.blb.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class Methodinterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("begin");
        Object ret= methodInvocation.proceed();//触发,执行核心
        System.out.println("end");
        return ret;
    }
}

通配切入点

根据表达式统配切入点

execution(修饰符 返回类型 切入点类 切入点方法(参数) 异常抛出)
    <aop:config>
<!--        匹配参数-->
        <aop:pointcut id="user2" expression="execution(* *(com.blb.entity.User))"/>
        
<!--匹配方法名(无参)-->
        <aop:pointcut id="user3" expression="execution(* queryUser())"/>
        
<!--匹配方法名(任意参数)-->
        <aop:pointcut id="user4" expression="execution(* queryUser(..))"/>
        
<!--匹配返回值类型 -->
        <aop:pointcut id="user5" expression="execution(java.util.List *(..))"/>
        
<!--匹配类名-->
        <aop:pointcut id="user6" expression="execution(* com.blb.service.UserService2Impl.*(..))"/>
        
<!--匹配包名-->
        <aop:pointcut id="user7" expression="execution(* com.blb.service.*.*(..))"/>
        
<!--匹配包名,以及子包名 -->
        <aop:pointcut id="user8" expression="execution(* com.blb..*.*(..))"/>
        
   <aop:advisor advice-ref="beforeadvice" pointcut-ref="user8"></aop:advisor>
    </aop:config>

AOP小结

通过AOP提供的编码流程,更便利的定制切面,更方便的定制动态代理
进而彻底解决了辅助代码功能冗余的问题
业务类中职责单一性得到更好的保障
辅助功能也有更好的复用性

JDK和CGLIB选择

spring底层,包含了JDK代理和cglib代理两种动态生成机制
基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用cglib代理

后处理器

整合mybatis

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>spring01</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>13</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
    </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>

        <!--spring AOP的包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.3</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.5</version>
        </dependency>

        <dependency>

            <groupId>com.alibaba</groupId>

            <artifactId>druid</artifactId>

            <version>1.1.16</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>

    <!--在build中配置resources,来防止我们资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

</project>

将数据资源配置到项目

引入jdbc.properties配置文件

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db3?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456
jdbc.init=1
jdbc.minIdle=1
jdbc.maxActive=10

整合spring配置文件和properties配置文件

Druid连接池可选参数

 <!--读取jdbc.properties  配置文件参数化(参数占位符)-->
    <context:property-placeholder location="classpath:jdbc.properties" />
    <!--    集成Druid连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--        基本配置-->
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>

        <!--        配置初始化大小,最大,最小-->
        <property name="initialSize" value="${jdbc.init}"></property>
        <property name="minIdle" value="${jdbc.minIdle}"></property>
        <property name="maxActive" value="${jdbc.maxActive}"></property>

        <!--         配置获取连接等待超时时间-->
        <property name="maxWait" value="60000"></property>

        <!--        配置间隔多久时间才能进行一次检测,检测需要关闭的空闲连接,单位是毫秒-->
        <property name="timeBetweenEvictionRunsMillis" value="60000"></property>

        <!--        配置一个连接池中最小生存时间,单位是毫秒-->
        <property name="minEvictableIdleTimeMillis" value="300000"></property>


    </bean>

导入依赖

//srping-jdbc
<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>

//srping+mybatis集成依赖
<dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.3</version>
        </dependency>

配置SqlSessionFactory


    <!--        生产sqlsessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--        注入连接池-->
        <property name="dataSource" ref="dataSource"></property>
        
<!--        注入Mapper文件信息,如果映射文件和Mapper接口,同包且同名,则此配置可以省略-->
        <property name="mapperLocations">
            <list>
                <value>classpath:com/blb/dao/*.xml</value>
            </list>
        </property>
<!--         定义别名-->
        <property name="typeAliasesPackage" value="com.blb.entity"></property>
    </bean>

配置MapperScannerConfigurer

管理dao里面的Mapper实现类的创建,并创建Mapper对象,存入工厂管理
扫描所有DAO接口,去构建DAO实现
将DAO实现存入工厂管理
DAO实现对象在工厂中的id是:首字母小写-接口的类名
例如 UserDAO===》userDAO
    OrderDAO===》orderDAO

dao接口所在的包,如果有多个包逗号或分号分隔
 <property name="basePackage" value="com.a.dao,com.b.dao"></property>
  <bean id="MapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  

        <property name="basePackage" value="com.blb.dao"></property>
        
<!--        如果工厂只有一个sqlsessionfactory的bean此配置可以省略-->
           <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>

配置service

package com.blb.service;

import com.blb.dao.StudentMapper;
import com.blb.entity.student;

import java.util.List;

public class StudentServiceImpl implements StudentService{
    private StudentMapper studentMapper;

    public StudentMapper getStudentMapper() {
        return studentMapper;
    }

    public void setStudentMapper(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }

    @Override
    public List<student> querystus() {
        return studentMapper.querystus();
    }
}


<bean id="studentService" class="com.blb.service.StudentServiceImpl">
        <property name="studentMapper" ref="studentMapper"></property>
    </bean>

事务

配置DataSourceTransactionManager

<!--    引入一个事务管理器,其中依赖DataSource,借以获取连接,进而控制事务逻辑-->
    <bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
注意 DataSourceTransactionManagerSqlSessionFactoryBean 要注入同一个DataSource的bean,否则事务控制失效

配置事务通知

基于事务管理器,进一步定制,生成一个额外的功能:AdviceAdvice可以切入任何需要事务的方法,通过事务管理器为方法控制事务
  <tx:advice id="txManger" transaction-manager="tx">
        <tx:attributes>
<!--             以User结尾的方法,切入此方法时,采用对应事务实行-->
            <tx:method name="*User" rollback-for="Exception"/>
<!--            以query开头的方法,切入此方法时,采用对应事务实行-->
            <tx:method name="query*" propagation="SUPPORTS"></tx:method>
<!--            剩余所有方法-->
            <tx:method name="*"></tx:method>
        </tx:attributes>
    </tx:advice>

事务属性

隔离级别

isolation 隔离级别

名称级别
default(默认值)采用数据库默认的设置
READ_COMMITTED读提交(oracle数据库默认的隔离界别)
READ_UNCOMMITTED读未提交
REPEATABLE_READ可重复读(MYSQL数据库默认的隔离级别)
SERIALIZABLE序列化读
隔离界别由低到高为:
READ_COMMITTED <READ_COMMITTED <REPEATABLE_READ <SERIALIZABLE 
特性
安全性:级别越高,多事务并发时,越安全,因为共享的数据越来越少,事务间彼此干扰减少
并发性:级别越高,多事务并发时,并发越差,因为共享的数据越来越少,事务间阻塞情况增多
并发问题

事务并发时的安全问题

问题描述
脏读一个事务读取到另一个事务还未提交的数据,大于等于READ_COMMITTED 可防止
不可重复读一个事务内多次读取一行数据的内容,其结果不一致,大于等于REPEATABLE_READ 可防止
幻影读一个事务内多次读取一张表中相同内容,其结果不一致SERIALIZABLE 可防止
传播行为
propagation 传播行为
当涉及到事务嵌套(Service调用service)时可以设置

SUPPORTS=不存在外部事务,则不开启新事务,存在外部事务,则合并到外部事物中(适合查询)
REQUIRED=不存在外部事务,则开启新事务,存在外部事物,则合并到外部事务中,(默认值,适合增删改)
读写性
read-only  读写性
true :只读 可以提高事务效率 (适合查询)
false :可读可写 (默认值,适合增删改)
事务超时
timeout=""  事务超时时间
当前事务所需要操作的数据被其他事务占用,则等待
xx 自定义等待时间xx()
-1 由数据库指定等待时间,(默认值)
事务回滚
rollback-for 回滚属性

如果抛出RuntimeException,则自动回滚
如果抛出CheckExcpton(非运行时异常),不会自动回滚,而是默认提交事务
处理方案将CheckExcpton转换成RunntimeExcpton上抛,或者设置rollback-for="Exception"

编织

将事务管理的Advice切入所需要的业务方法中

 <aop:config>
        <aop:pointcut id="pc" expression="execution(* com.blb.service.StudentServiceImpl.*(..))"/>
        <aop:advisor advice-ref="txManger" pointcut-ref="pc"></aop:advisor>
    </aop:config>

注解开发

除了在applicationContext.xml中配置装配信息,我们还可以通过注解的方式来配置装配信息。

配置注解的支持

<context:annotation-config />

定义要扫描的包

<context:component-scan base-package="com.blb.spring.bean" /> 

扫描com.blb.spring.bean包,让这个包下的所有注解生效。可以定义多个包用逗号隔开。

声明bean

用于替换自建类型组件的< bean>,可以快速的声明bean

@Service 业务类专用
@Repository dao实现类专用
@Controller web层专用

@Component 通用

@Scope 用户控制bean的创建方式

@Component

@Component注解用来声明bean,相当于配置文件中的<bean id="" class="" />,其中id值默认为当前类的类名的首字母小写,class默认为当前类的类型。如果不想使用默认的id值,可以加参数来自定义,方式为@Component("newName")

@Service

声明bean,此类以一个业务类,需要将此类纳入工厂,可以等价替换掉 < bean >标签
这个注解是给类添加的

@Service
public class UserServiceImpl implements UserService{

}
//@Service默认beanId==首字母小写的类名 userServiceImpl
//@Service("xxx") 自定义beanId为xxx

@Controller、@Repository基本与@Service()一样,这3个注解的作用同@Component,只是分别用在控制层、服务层、DAO层。语义化可读性强。

@Scope

声明创建的模式,默认为单例模式

@Scope
public class UserServiceImpl implements UserService{

}

@Scope("singleton") 声明创建的模式为单例模式

@Scope("prototype") 声明创建模式为多例模式

注入(DI)

用于完成bean中属性值的注入

@Autowired 基于类型自动注入

@Resource 基于名称自动注入

@Qualifier("userDAO") 限定要自动注入的bean的id,一般和@Autowired一起使用

@Value 注入简单类型数据(jdk8种+string)

@Autowired不能唯一装配时,需要@Autowired+@Qualifier

@Resource

名称自动注入

@Resource 
private UserDao userDao;

如果不加name会默认找和属性同名的bean
@Resource (name="xx")会在工厂中找一个id为xx的bean

如果爆红则需要引入依赖

<!--注解依赖 https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api -->
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>

@Autowired

基于类型自动注入
要么找不到,要么找到多个报错

基于类型自动注入,并挑选Beanid=userDAO
@Autowired
@Qualifier("userDAO") 

@Value

将值注入到bean中,可以定义在属性上,也可以定义在set方法或者其它方法上。

@value"100"//注入数字
private Integer id; 
@value"dyk"//注入string
private String name; 
@Component
public class User { 
    //相当于<property name="name" value="dyk"/> 
    @value("dyk") 
    public String name; 
    
    //也可以放在set方法上面
    //@value("dyk")
    public void setName(String name) { 
        this.name = name; 
    }
}
  • @Value可以用在属性上注入,也可以用在方法上注入。
  • 方法注入时方法名不限制,注入方法会调用。
  • 属性注入时不依赖于构造方法跟setter方法

区别:

@Resource和@Autowired的区别:

  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired通过byType的方式实现,而且必须要求这个对象存在!【常用】
  • @Resource默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!【常用】
  • 执行顺序不同:@Autowired通过byType的方式实现。@Resource默认通过byname的方式实现
  • @Autowired来自Spring,@Resource来自JDK

小结:

  • 需要配置注解的支持。
  • 注解装配的机制是直接反射,不依赖于bean类的set方法跟构造方法
  • 自动装配会在配置文件中查看到需要的bean然后帮我们装配,所以配置文件中需要有符合byType或者byName模式的bean。
  • 注解装配的方式要求必须装配上,如果没有符合要求的bean来装配则会报错,除非在方法上使用require=false参数:@Autowired(required=false)
  • 如果@Autowired在配置文件中通过byName匹配不上,通过byType有多个匹配上的话,可以配合注解@Qualifier(value="course2")来定义我们要装配那个id属性值对应的bean。
  • @Autowired注解是来自spring的,我们还可以通过@Resource注解来实现。作用相同。@Resource(name="course2")@Autowired+@Qualifier(value="course2")

JavaConfig

JavaConfig是Spring的一个子项目,在Spring4之后成为了一个核心功能,主要作用是不用配置文件,全部在java中配置完成

entity

@Component
public class Student {
	
	@Value("seven")
	private String name  ;
	
	@Value("18")
	 private int age ;
	// setter/getter/toString	 
	
}

AppConfig

//@Configuration代表一个配置类(applicationContext.xml),本质上也是一个Component
@Configuration  
// @ComponentScan扫描某个包,相当于<context:component-scan  />
@ComponentScan("com.blb.entity")
// @Import导入另一个配置类,相当于<import />
@Import(AppConfig2.class)
public class AppConfig {
	
	/**
	 * @Bean标签相当于<bean     />
	 * id值默认为方法名,class默认为返回对象的类型
	 */
	
	@Bean
	public Student student(){
		return new Student();// 返回的对象实例注册到容器中
	}

}
	@Test
public void testStudent(){
    //使用的是java注解配置方式,需要使用AnnotationConfigApplicationContext
    ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    Student s =  context.getBean("student",Student.class);
    System.out.println(s);
}
  • @Configuration等同于配置文件中的beans标签
  • @Bean等同于配置文件中的bean标签
  • @ComponentScan扫描某个包,让包下的注解生效
  • @Import导入多个带@Configuration注解的配置类

事务控制

用于控制事务切入
@Transactional
工厂配置中的<tx:advice><aop:config>可以省略
可以给类添加也可以给类对应的方法添加
可以给类添加各类中的每个方法都切入事务(有自己的事务控制的方法除外)
给方法添加该方法的事务控制,仅对此方法有效
@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED,readOnly = false,rollbackFor = Exception.class,timeout = -1)

注解所需配置

<!--    告知spring,那些包中有别注解的类,方法,属性-->
<!--    如果有多个包逗号或分号分隔<context:component-scan base-package="com.blb,com.xxx"></context:component-scan>-->
<context:component-scan base-package="com.blb"></context:component-scan>

<!--告知spirng@transactional在制定事务时基于tx= DataSourceTransactionManager   -->
<tx:annotation-driven transaction-manager="tx"></tx:annotation-driven>

aop

注解使用

package com.blb.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect//声明此类是一个切面类:会包含切入点(pointcut)和通知(advice)
@Component //声明组件进入工厂
public class myaspect {
    //定义切入点
    @Pointcut("execution(* *(..))")
    public  void pc(){}
    
    @Before("pc()")//前置通知
    public void mybefore(JoinPoint a){
        System.out.println("target"+a.getTarget());
        System.out.println("args"+a.getArgs());
        System.out.println("method name"+a.getSignature().getName());
        System.out.println("before...");
        
    }
    @AfterReturning(value = "pc()",returning="ret")//后置通知
    public void myAfterReturning(JoinPoint a,Object ret){
        System.out.println("after....");
    }
    @Around("pc()")
    public Object myInterceptor(ProceedingJoinPoint p) throws Throwable {
        System.out.println("begin...");
        Object ret=p.proceed();
        System.out.println("end...");
        return ret;
        
    }
    @AfterThrowing(value = "pc()",throwing = "ex")//异常通知
    public void myThrows(JoinPoint jp,Exception ex){
        System.out.println("throws...");
        System.out.println(ex.getMessage());
    }
}

或者直接在注解里面配置

@Aspect  //标注这个类是一个切面
public class myaspect  {
	
    @Before("execution(* service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("=====方法执行前=====");
    }

    @After("execution(* service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("=====方法执行后=====");
    }

    //在环绕增强中,我们可以给地暖管一个参数,代表我们要获取切入的点
    @Around("execution(* service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前");

        Object proceed = joinPoint.proceed();

        System.out.println("环绕后");
    }
}

配置

<!--    告知spring,那些包中有别注解的类,方法,属性-->
<!--    如果有多个包逗号或分号分隔<context:component-scan base-package="com.blb,com.xxx"></context:component-scan>-->
<context:component-scan base-package="com.blb"></context:component-scan>

<!--添加如下配置,启用aop注解-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

使用java的方式来配置spring

//这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器中 
@component 
public class User { 
    private String name;
    
    public String getName() { 
    	return name; 
    } 
    //属性注入值
    @value("dyk")  
    public void setName(String name) { 
    	this.name = name; 
    } 
    @Override 
    public String toString() { 
        return "user{" + 
        "name='" + name + '\''+ 
        '}'; 
    } 
}

要么使用@Bean,要么使用@Component和ComponentScan,两种效果一样

//这个也会Spring容器托管,注册到容器中,因为他本来就是一个@Component 
// @Configuration表这是一个配置类,就像我们之前看的beans.xml,类似于<beans>标签
@Configuration 
@componentScan("com.blb.dao") //开启扫描
public class Config { 
    //注册一个bean , 就相当于我们之前写的一个bean 标签 
    //这个方法的名字,就相当于bean 标签中的 id 属性 ->getUser
    //这个方法的返同值,就相当于bean 标签中的class 属性 ->User
    
    //@Bean 
    public User getUser(){ 
    	return new User(); //就是返回要注入到bean的对象! 
    } 
}
 @Bean是相当于< bean>标签创建的对象,@Component是通过spring自动创建的这个被注解声明的对象,所以这里相
 当于有两个User对象被创建了。一个是bean标签创建的(@Bean),一个是通过扫描然后使用@Component,spring自
 动创建的User对象,所以这里去掉@Bean这些东西,然后开启扫描。之后在User头上用@Component即可达到spring自
 动创建User对象了

spring集成JUnit

导入依赖

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.7.RELEASE</version>
</dependency>

<dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
</dependency>

编码

可以免去工厂创建过程
可以直接将要测试的主键注入到测试类
package com.blb.tests;

import com.blb.dao.UserDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)//由SpringJUnit4ClassRunner启动测试
@ContextConfiguration("classpath:spring-context.xml")
public class testjunit {//当前测试类也会被纳入工厂中,所以其中属性可以注入

    @Autowired//注入要测试的组件
    @Qualifier("userDao")
    private UserDao userDao;

    @Test
    public void test()
    {
        userDao.deleteUser(1);
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值