Spring学习完个人总结

Spring个人总结

通过观看尚硅谷的视频学习完,自己在学习中归纳的基本知识

一、基于xml的方式配置bean

(一)基于Xml的方式配置bean

1、执行

//1. 创建spring的ioc容器
//springXml 为spring的xml配置文件
String springXml = "spring-config.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(filePath);

//2. 从ioc容器中获取bean实例
//用id获取bean是唯一的
User user = (User) ctx.getBean("user");
//用Class获取若有多个bean会出现异常
//User user = (User) ctx.getBean(User.class);
user.setName("Spring2");
user.setAge(13);
//3. 调用实例方法
System.out.println(user.toString());

2、javabean

1)学生
//该用户
public class User {
    
    
    private String name;
    private Integer age;
    private Double mark;

    public User() {
    
    
    }

    public User(String name, Integer age) {
    
    
        this.name = name;
        this.age = age;
    }

    public User(String name, Double mark) {
    
    
        this.name = name;
        this.mark = mark;
    }

    public User(String name, Integer age, Double mark) {
    
    
        this.name = name;
        this.age = age;
        this.mark = mark;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public Integer getAge() {
    
    
        return age;
    }

    public void setAge(Integer age) {
    
    
        this.age = age;
    }

    public Double getMark() {
    
    
        return mark;
    }

    public void setMark(Double mark) {
    
    
        this.mark = mark;
    }

    @Override
    public String toString() {
    
    
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", mark=" + mark +
                '}';
    }
}
2)教师
//该教师类
public class Teacher {
    
    
    private String name;
    private Integer age;
    private User user;

    public Teacher(){
    
    
    }

    public Teacher(String name, Integer age, User user) {
    
    
        this.name = name;
        this.age = age;
        this.user = user;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public Integer getAge() {
    
    
        return age;
    }

    public void setAge(Integer age) {
    
    
        this.age = age;
    }

    public User getUser() {
    
    
        return user;
    }

    public void setUser(User user) {
    
    
        this.user = user;
    }

    @Override
    public String toString() {
    
    
        return "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", user=" + user +
                '}';
    }
}
3)list集合
//集合类
public class CollectionClass {
    
    
    private List<User> userList;
    private List<Teacher> teacherList;

    public CollectionClass(){
    
    

    }

    public CollectionClass(List<User> userList, List<Teacher> teacherList) {
    
    
        this.userList = userList;
        this.teacherList = teacherList;
    }

    public List<User> getUserList() {
    
    
        return userList;
    }

    public void setUserList(List<User> userList) {
    
    
        this.userList = userList;
    }

    public List<Teacher> getTeacherList() {
    
    
        return teacherList;
    }

    public void setTeacherList(List<Teacher> teacherList) {
    
    
        this.teacherList = teacherList;
    }

    @Override
    public String toString() {
    
    
        return "CollectionClass{" +
                "userList=" + userList +
                ", teacherList=" + teacherList +
                '}';
    }
}
4)map
//map类
public class MapClass {
    
    
    private Map<Teacher,User> teacherUserMap;

    public MapClass(){
    
    

    }

    public MapClass(Map<Teacher, User> teacherUserMap) {
    
    
        this.teacherUserMap = teacherUserMap;
    }

    public Map<Teacher, User> getTeacherUserMap() {
    
    
        return teacherUserMap;
    }

    public void setTeacherUserMap(Map<Teacher, User> teacherUserMap) {
    
    
        this.teacherUserMap = teacherUserMap;
    }

    @Override
    public String toString() {
    
    
        return "MapClass{" +
                "teacherUserMap=" + teacherUserMap +
                '}';
    }
}
5)properties
//properties类
public class PropertiesClass {
    
    
    private Properties properties;

    public Properties getProperties() {
    
    
        return properties;
    }

    public void setProperties(Properties properties) {
    
    
        this.properties = properties;
    }

    @Override
    public String toString() {
    
    
        return "PropertiesClass{" +
                "properties=" + properties +
                '}';
    }
}
6)peaple类
public class Peaple {
    
    
    private String name;
    private Double mark;

    public Peaple(String name, Double mark) {
    
    
        this.name = name;
        this.mark = mark;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public Double getMark() {
    
    
        return mark;
    }

    public void setMark(Double mark) {
    
    
        this.mark = mark;
    }

    @Override
    public String toString() {
    
    
        return "Peaple{" +
                "name='" + name + '\'' +
                ", mark=" + mark +
                '}';
    }
}

3、xml片段

1)通过setter配置bean中属性
<!-- 通过property标签用setter来配置bean中属性 -->   
<!-- 
	id 在xml中的唯一标识
	class 这个bean的类型
-->
<bean id="user" class="com.mySpring.test.User">
    <!-- 
		name setter修改的属性名(例如:setXxx 即 name=“xxx”)
		value 将该值赋予该属性
		ref 通过引用创建的bean来赋值
	-->
    <!-- 也可以使用其子节点ref的bean属性进行联系 -->
	<property name="name" value="Spring" ></property>
	<property name="age" value="12"></property>
</bean>
2)通过构造器配置bean中属性
<!-- 通过构造方法来配置bean中属性 -->
<!-- 使用构造器注入属性值可以指定参数的位置和参数的类型以区分重载的构造器 -->
<bean id="user2" class="com.mySpring.test.User">
    <!--
		value 表示想要通过构造函数传入的参数
		type 表示该值的类型
	-->
    <constructor-arg value="zhangsan" type="java.lang.String"></constructor-arg>
    <constructor-arg value="12" type="java.lang.Integer"></constructor-arg>
</bean>

3)配置集合属性
<!-- 为集合属性赋值 -->
<bean id="collection1" class="com.mySpring.test.CollectionClass">
    <property name="userList">
        <!-- 用list标签中的ref来引用之前已创建的bean,也可以使用内部bean创建新的bean对象(麻烦) -->
        <list>
            <ref bean="user"></ref>
            <ref bean="user2"></ref>
            <ref bean="user3"></ref>
            <ref bean="user4"></ref>
        </list>
    </property>
    <property name="teacherList">
        <list>
            <ref bean="teacher1"></ref>
            <ref bean="teacher2"></ref>
            <ref bean="teacher3"></ref>
            <ref bean="teacher4"></ref>
            <ref bean="teacher5"></ref>
        </list>
    </property>
</bean>
<!-- 配置map属性 -->
<bean id="map1" class="com.mySpring.test.MapClass">
    <property name="teacherUserMap">
        <!--
			map标签中的entry来为map赋值
				key-ref 键
				value-ref 值
		-->
        <map>
            <entry key-ref="teacher1" value-ref="user"></entry>
            <entry key-ref="teacher2" value-ref="user2"></entry>
            <entry key-ref="teacher3" value-ref="user3"></entry>
        </map>
    </property>
</bean>
<!-- 配置properties -->
<bean id="properties1" class="com.mySpring.test.PropertiesClass">
    <property name="properties">
        <props>
            <prop key="users">root</prop>
            <prop key="password">123456</prop>
            <prop key="Url">jdbc:mysql:///test</prop>
            <prop key="DriverClass">com.mysql.jdbc.Driver</prop>
        </props>
    </property>
</bean>

4、细节

1)特殊符号
<bean id="user4" class="com.mySpring.test.User">
    <!-- 特殊符号需要用<![CDATA[xxxx]]进行表示 -->
    <constructor-arg type="java.lang.String">
        <value><![CDATA[<zhangsan>]]></value>
    </constructor-arg>
    <constructor-arg value="19" type="java.lang.Integer"></constructor-arg>
    <!-- 可以用constructor-arg标签下的value进行赋值 -->
    <!-- 若参数为空即可用null标签标识 -->
    <constructor-arg type="java.lang.Double">
        <value>19.9</value>
    </constructor-arg>
</bean>

2)内部bean
<bean id="teacher3" class="com.mySpring.test.Teacher">
    <property name="name" value="lisi3"></property>
    <property name="age" value="23"></property>
    <!-- 内部bean -->
    <property name="user">
        <bean class="com.mySpring.test.User">
            <constructor-arg value="wangwu"></constructor-arg>
            <constructor-arg value="12" type="java.lang.Integer"></constructor-arg>
            <constructor-arg value="98" type="java.lang.Double"></constructor-arg>
        </bean>
    </property>
</bean>

3)级联属性赋值
<bean id="teacher5" class="com.mySpring.test.Teacher">
    <constructor-arg value="zhaoliu5"></constructor-arg>
    <constructor-arg value="29" type="java.lang.Integer"></constructor-arg>
    <constructor-arg ref="user"></constructor-arg>
    <!-- 为级联属性赋值 注意:属性要先初始化后才可以赋值 -->
    <property name="user.mark" value="11"></property>
</bean>

5)配置单独的集合bean
<!-- 配置单独的集合bean以供多个bean进行引用 -->
<util:list id="users">
    <ref bean="user"></ref>
    <ref bean="user2"></ref>
    <ref bean="user3"></ref>
    <ref bean="user4"></ref>
</util:list>
<util:list id="teachers">
    <ref bean="teacher1"></ref>
    <ref bean="teacher2"></ref>
    <ref bean="teacher3"></ref>
    <ref bean="teacher4"></ref>
    <ref bean="teacher5"></ref>
</util:list>

6)p命名空间
<!-- 通过p命名空间对属性进行赋值 -->
<bean id="collection3" class="com.mySpring.test.CollectionClass" p:userList-ref="users" p:teacherList-ref="teachers">
</bean>

7)xml自动填充属性
<!-- 通过autowire自动装配属性(用Byname时id要跟javabean风格的属性名一致才可自动装配) -->
<!-- user的bean之前的mark属性被property赋值过 -->
<bean id="teacher6" class="com.mySpring.test.Teacher"
p:name="laoliu" p:age="55" autowire="byName"></bean>

<!-- bytype跟具类型来自动装配,当有多个bean时不可用 -->
<bean id="teacher7" class="com.mySpring.test.Teacher" 
      p:name="laoqi" p:age="55" autowire="byType"></bean>

(二)bean之间的关系

    <!-- 抽象bean不能被IOC实例化,只能用来被继承配置
     若一个bean没有class属性,则该bean必须是一个抽象bean -->
    <bean id="user" class="com.mySpring.parent.User" abstract="true"
    p:name="wujinlong" p:age="15" p:mark="99"></bean>

    <!-- 通过parent来继承配置 -->
    <bean id="user2" parent="user"></bean>

    <!-- 依赖用depends-on来表示,该bean必须在有被依赖对象bean时才能使用,否则报异常 -->
    <bean id="teacher" class="com.mySpring.parent.Teacher"
          p:name="laoshiliu" p:age="53" depends-on="user2"></bean>

(三)bean作用域

<!-- 
	scope属性如果是singleton表示在IOC容器初始化时创建bean实例,IOC容器整个生命周期中只创建一个bean,单例
    prototype表示容器初始化时不创建bean实例,而在每一次请求时创建一个新的bean实例,并返回 
-->
<bean id="user" class="com.mySpring.scope.User" scope="singleton"
p:name="zuieyuyi" p:age="24" p:mark="95"></bean>

(四)使用外部文件

1)引入外部文件
<!-- 需要引入解析文件才可以使用 -->
<!-- 需context命名空间 --> 
<context:property-placeholder location="db.properties"></context:property-placeholder>

2)使用外部文件中属性
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!-- 使用外部属性文件 ${外部文件中的属性名} -->
    <property name="user" value="${user}"></property>
    <property name="password" value="${password}"></property>
    <property name="driverClass" value="${driverclass}"></property>
    <property name="jdbcUrl" value="${jdbcurl}"></property>
</bean>

3)外部文件

数据库连接信息

user=root
password=123456
driverclass=com.mysql.cj.jdbc.Driver
jdbcurl=jdbc:mysql:///test_db?characterEncoding=utf-8&serverTimezone=UTC&rewriteBatchedStatements=true

(五)SpEL

<bean id="car" class="com.mySpring.SpEl.Car">
    <!-- 使用SpEL赋字面值 -->
    <property name="brand" value="#{
     
     'baoma'}"></property>
    <property name="price" value="#{300000}"></property>
    <!-- 使用SpEL调用静态方法 -->
    <property name="wheel" value="#{T(java.lang.Math).PI*10}"></property>
</bean>

<bean id="peaple" class="com.mySpring.SpEl.Peaple">
    <property name="name" value="zhangsan"></property>
    <!-- 使用SpEL来使用bean -->
    <property name="car" value="#{car}"></property>
    <!-- 简单的判断 -->
    <property name="city" value="#{car.price>=200000?'北京':'其他'}"></property>
</bean>

(六)bean生命周期

1)xml文件
<!--
    init-method 指定初始化方法
    destroy-method 指定销毁方法
-->
<bean id="peaple" class="com.mySpring.lifecycle.Peaple"
init-method="init" destroy-method="destroy">
    <property name="name" value="lisi"></property>
</bean>

<!--
    实现BeanPostProcessor接口,并具体提供
    Object postProcessAfterInitialization(Object bean, String beanName) :在init-method之前被调用
    Object postProcessBeforeInitialization(Object bean, String beanName):在init-method之后被调用
    的实现

    参数:
    bean:bean实例本身
    beanName:IOC容器配置的bean名字
    返回值:
    是实际上返回给用户的那个bean,注意:可以在以上两个方法中修改返回的bean,甚至返回个新的bean
-->
<!-- 配置bean的后置处理器 -->
<bean class="com.mySpring.lifecycle.MyBeanPostProcessor"></bean>

2)前置后置处理器
public class MyBeanPostProcessor implements BeanPostProcessor {
    
    
    /**
     *
     * @param bean bean对象
     * @param beanName bean对象的名字
     * @return 经过后置处理后的bean对象
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
        System.out.println("postProcessAfterInitialization:"+bean+","+beanName);
        return bean;
    }
    
    /**
     * 该方法在bean被创建之前调用
     * @param bean bean对象
     * @param beanName bean对象的名字
     * @return 经过前置处理后的bean对象
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
        System.out.println("postProcessBeforeInitialization:"+bean+","+beanName);
        return bean;
    }
}

3)运行结果
constractor.......
setname.....
postProcessBeforeInitialization:com.mySpring.lifecycle.Peaple@192b07fd,peaple
init .....
postProcessAfterInitialization:com.mySpring.lifecycle.Peaple@192b07fd,peaple
com.mySpring.lifecycle.Peaple@192b07fd
destroy .......

(七)工厂方法配置bean

1)实例工厂配置
<!-- 配置工厂实例 -->
<bean id="instanceFactory" class="com.mySpring.factory.InstanceFactory"></bean>

<!-- 通过实例工厂方法来配置bean -->
<!--
    factory-bean:指向实例工厂方法的bean
    factory-method:指向实例工厂方法的名字
    constructor-arg:如果实例工厂方法需要传入参数,则使用cansturctor-arg来配置参数
-->
<bean id="peaple2" factory-bean="instanceFactory" factory-method="getPeaple">
    <constructor-arg name="name" value="wangwu"></constructor-arg>
</bean>

//实例工厂类
public class InstanceFactory {
    
    
    private Map<String,Peaple> map = null;
    public InstanceFactory(){
    
    
        map = new HashMap<>();
        map.put("lisi",new Peaple("lisi",99.0));
        map.put("wangwu",new Peaple("wangwu",99.0));
    }

    //实例工厂方法
    public Peaple getPeaple(String name){
    
    
        return map.get(name);
    }
}

2)静态工厂类
<!-- 通过静态工厂方法来配置bean,注意:不是配置静态工方法实例,而是配置bean实例 -->
<!--
    class 属性:指向静态工厂方法的全类名
    factory-method:指向静态工厂方法的名字
    constructor-arg:如果静态工厂方法需要传入参数,则使用cansturctor-arg来配置参数
-->
<bean id="peaple1" class="com.mySpring.factory.StaticFactory"
factory-method="getPeaple">
    <constructor-arg value="lisi"></constructor-arg>
</bean>

//静态工厂类
public class StaticFactory {
    
    
    private  static Map<String,Peaple> map = new HashMap<>();

    static{
    
    
        map.put("lisi",new Peaple("lisi",99.0));
        map.put("wangwu",new Peaple("wangwu",99.0));
    }

    //静态工厂方法
    public static Peaple getPeaple(String name){
    
    
        return map.get(name);
    }

}

3)通过FactoryBean配置bean
<!-- 通过factorybean来配置bean的实例 -->
<!--
    class:指向factorybean的全类名
    property:配置factorybean的属性

    但实际返回的实例却是factorybean的getObject()方法返回的实例!
-->
<bean id="peaple" class="com.mySpring.factorybean.FactoryPeapleBean">
    <property name="name" value="lisi"></property>
</bean>

//实现FactoryBean接口
public class FactoryPeapleBean implements FactoryBean<Peaple> {
    
    
    private String name;

    public void setName(String name) {
    
    
        this.name = name;
    }

    //返回bean的对象
    @Override
    public Peaple getObject() throws Exception {
    
    
        return new Peaple(name,93.0);
    }

    /**
     * 返回bean的类型
     * @return bean的类型
     */
    @Override
    public Class<?> getObjectType() {
    
    
        return Peaple.class;
    }

    /**
     * 配置是否是一个单实例的bean
     * @return
     */
    @Override
    public boolean isSingleton() {
    
    
        return false;
    }
}

二、通过注解配置bean

(一)注解

–@Component: 基本注解, 标识了一个受 Spring 管理的组件

–@Respository: 标识持久层组件

–@Service: 标识服务层(业务层)组件

–@Controller: 标识表现层组件

1)@Component注解
@Component
public class TestObject {
    
    
    public void test(){
    
    
        System.out.println("TestObject test .....");
    }
}

2)@Respository注解
@Repository("testRepository")
public class TestRepositoryImpl implements TestRepository {
    
    

    //如果可以允许这个属性不被设置需要注解@Autowired(required = false)
    @Autowired(required = false)
    private TestObject testObject;

    @Override
    public void insert() {
    
    
        System.out.println("testRepository add....");
        System.out.println(testObject);
    }
}

3)@Service注解
//@Autowired通过类型的自动填装
//当IOC容器中有多个可填装的类时,默认会报异常
//处理方式
// 1、为填装类的注释添加属性名
// 2、用@Qualifier 注释要装配的类
@Autowired
@Qualifier("testRepositoryImpl2")
private TestRepository testRepository;

public void add(){
    
    
    System.out.println("testService add ....");
    testRepository.insert();
}

4)@Controller注解
@Controller
public class TestController {
    
    
    @Autowired
    private TestService testService ;

    public void execute(){
    
    
        System.out.println("TestService execute ....");
        testService.add();
    }
}

(二)自动注入配置

<!-- 指定IOC容器扫描的包 -->
<!-- resource-pattern:只扫描满足其中指定扫描的资源 -->
<!--
<context:component-scan base-package="com.mySpring.annotation"
resource-pattern="repository/*.class"></context:component-scan>
-->

<!-- context:exclude-filter 子节点指定排除那些指定表达式的组件 -->
<!-- context:include-filter 子节点指定包含那些表达式的组件,该子节点需要use-default-filters配合使用 ______ use-default-filters="false"-->
<context:component-scan base-package="com.mySpring.annotation">
    <!-- 不包含@Repository注释的类 -->
<!--        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
    <!-- 包含@Repository注释的类 -->
<!--        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>-->
    <!-- 表示不包含这个接口以及这个接口的实现类 -->
<!--        <context:exclude-filter type="assignable" expression="com.mySpring.annotation.repository.TestRepository"/>-->
    <!-- 表示不包含这个接口以及这个接口的实现类 -->
<!--        <context:include-filter type="assignable" expression="com.mySpring.annotation.repository.TestRepository"/>-->
</context:component-scan>

(三)组件装配

1)@Autowired

•@Autowired 注解自动装配具有兼容类型的单个 Bean属性

–构造器,普通字段(即使是非public),一切具有参数的方法都可以应用@Authwired注解

–默认情况下,所有使用@Authwired注解的属性都需要被设置.当Spring找不到匹配的Bean装配属性时,会抛出异常,若某一属性允许不被设置,可以设置@Authwired注解的required属性为false

@Repository("testRepository")
public class TestRepositoryImpl implements TestRepository {
    
    

    //如果可以允许这个属性不被设置需要注解@Autowired(required = false)
    @Autowired(required = false)
    private TestObject testObject;

    @Override
    public void insert() {
    
    
        System.out.println("testRepository add....");
        System.out.println(testObject);
    }
}

2)@Qualifier

@Qualifier 注解里提供 Bean 的名称. Spring 允许对方法的入参标注 **@**Qualifiter 已指定注入 Bean 的名称

//@Autowired通过类型的自动填装
//当IOC容器中有多个可填装的类时,默认会报异常
//处理方式
// 1、为填装类的注释添加属性名
// 2、用@Qualifier 注释要装配的类
@Autowired
@Qualifier("testRepositoryImpl2")
private TestRepository testRepository;

public void add(){
    
    
    System.out.println("testService add ....");
    testRepository.insert();
}

(四)泛型依赖注入

在这里插入图片描述

1)BaseService
public class BaseService<T> {
    
    

    @Autowired
    private BaseRepository<T> baseRepository;

    public void add(){
    
    
        System.out.println("BaseService add ...");
        System.out.println(baseRepository);
    }

}

son

@Service
public class TestService extends BaseService<User> {
    
    
}

2)BaseRepository
public class BaseRepository<T> {
    
    
}

son

@Repository
public class TestRepository extends BaseRepository<User>{
    
    
}

3)main
//spring4 泛型依赖注入
public class GenericMain {
    
    
    public static void main(String[] args) {
    
    
        ApplicationContext ctx = new ClassPathXmlApplicationContext("springGeneric-config.xml");

        TestService testService = (TestService) ctx.getBean("testService");
        //子类调用父类方法
        testService.add();
    }
}

4)结果

自动填装父类依赖对象的子类

BaseService add ...
com.mySpring.generic.TestRepository@64d7f7e0

三、AOP基础

需引入

aopalliance-1.0.jar
spring-aop-4.2.1.RELEASE.jar

(一)代理对象

1)代理对象
//实现InvocationHandler,来实现动态代理
public class PrintProxy implements InvocationHandler {
    
    
    //要有代理对象的属性
    private PrintInterface printer;

    //获取代理的对象
    public PrintInterface newProxyInstace(PrintInterface printer){
    
    
        this.printer = printer;

        //printer.getClass().getClassLoader() 代理类的类加载器
        //printer.getClass().getInterfaces() 代理类的实现接口
        //this InvocationHandler 绑定代理类的方法
        PrintInterface instance = (PrintInterface) Proxy.newProxyInstance(printer.getClass().getClassLoader(), printer.getClass().getInterfaces(), this);

        return  instance;
    }

    //代理的目的:使对象在调用方法时可以对调用进行记录通知等操作,不需要去修改类源代码
//    proxy,代理后的实例对象。
//    method,对象被调用方法。
//    args,调用时的参数。
// 返回值一般是方法产生的结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        if("print".equals(method.getName())){
    
    
            //前置通知
            System.out.println("start ........ start");

            try{
    
    
                Object result = method.invoke(printer, args);
                //返回通知
            }catch(Exception e){
    
    
                e.printStackTrace();
                //异常通知,可以访问到方法出现的异常
            }

            //后置通知,因为方法可能会出异常访问不到
            System.out.println("printover ........ printover");
        } else if ("write".equals(method.getName())) {
    
    
            System.out.println("start ........ start");
            method.invoke(printer,args);
            System.out.println("writeover ........ writeover");
        }
        return null;
    }
}

(二)基于注解的方式配置aop(基于AspectJ注解)

1、启动

<!-- 使AspectJ注解起作用:自动为匹配的类生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

2、注解

1)切点类(@Aspect)
//把这个类声明为一个切面:需要把该类放到IOC容器中,在声明为一个切面
@Aspect
@Component
public class LoggingAspect {
    
    
}

2)切入点(@Pointcut(“execution(public int Calculator.*(int,int))”))
//定义一个用于声明恰如点的表达式,一般该该方法中不需添入其代码
//使用@Pointcut来声明切入点表达式
//后面的其他通知直接使用方法名来引用切入点表达式。
@Pointcut("execution(public int Calculator.*(int,int))")
public  void declareJoinPointExpression(){
    
    }

3)优先级(@Order(2))
//order设置优先级,值越小优先级越高
@Order(1)
@Before("LoggingAspect.declareJoinPointExpression()")
public void checkargs(JoinPoint joinPoint){
    
    
    System.out.println("--->check: " + Arrays.asList(joinPoint.getArgs()));
}

4)前置通知(@Before(“declareJoinPointExpression()”))
//声明该方法是一个前置通知:在目标方法开始之前执行
//joinpoint 表连接点类(可以通过接点类获取该方法的方法名,参数值等)
@Order(2)
@Before("declareJoinPointExpression()")
public void beforeMethod(JoinPoint joinPoint){
    
    
    //方法名
    String methodName = joinPoint.getSignature().getName();
    //参数值
    List<Object> args = Arrays.asList(joinPoint.getArgs());
    System.out.println("the method " + methodName + " begins "+ args);

}

5)后置通知(@After(“declareJoinPointExpression()”))
//后置通知:在目标方法执行后(无论是否发生异常),执行通知。
//在后置通知中还不能访问目标方法执行的结果
@After("declareJoinPointExpression()")
public void afterMethod(JoinPoint joinPoint){
    
    
    String methodName = joinPoint.getSignature().getName();
    System.out.println("The method " + methodName + " ends ");
}

6)返回通知(@AfterReturning(value = “declareJoinPointExpression()”,returning = “result”))
//返回通知
@AfterReturning(value = "declareJoinPointExpression()",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){
    
    
    String methodName = joinPoint.getSignature().getName();
    System.out.println("The method " + methodName + " result " + result);
}

7)异常通知 (@AfterThrowing(value = “declareJoinPointExpression()”,throwing = “ex”))
//异常通知
@AfterThrowing(value = "declareJoinPointExpression()",throwing = "ex")
public void afterThrowing(JoinPoint joinPoint,Exception ex){
    
    
    String methodName = joinPoint.getSignature().getName();
    System.out.println("The method " + methodName + " result " + ex);
}

8)环绕通知(@Around(“execution(public int Calculator.*(int,int))”))
//环绕通知
//环绕通知需要携带 ProceedingJoinPoint 类型的参数
//环绕通知类似于动态代理的全过程:ProceedingJoinPoint 类型参数可以决定是否执行目标方法
//且必须有返回值,返回值为目标方法返回值
@Around("execution(public int Calculator.*(int,int))")
public Object around(ProceedingJoinPoint proceedingJoinPoint){
    
    
    Object result = null;
    String methodName = proceedingJoinPoint.getSignature().getName();
    List<Object> args = Arrays.asList(proceedingJoinPoint.getArgs());

    //执行目标方法
    try {
    
    
        //前置通知
        System.out.println("the method " + methodName + " begins "+ args);

        result = proceedingJoinPoint.proceed();

        //返回通知通知
        System.out.println("The method " + methodName + " result " + result);
    } catch (Throwable e) {
    
    
        e.printStackTrace();
        //异常通知
        System.out.println("The method " + methodName + " result " + e);
    }

    //后置通知
    System.out.println("The method " + methodName + " ends ");

    return result;
}

(三)基于配置文件的方式来配置AOP

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="calculator" class="com.mytest.proxy.springaoptest.xmlaspect.CalculatorImpl"></bean>

    <bean id="loggingAspect" class="com.mytest.proxy.springaoptest.xmlaspect.LoggingAspect"></bean>
    <bean id="checkAspect" class="com.mytest.proxy.springaoptest.xmlaspect.CheckAspect"></bean>

    <!-- 配置aop -->
    <aop:config>
        <!-- 配置切点表达式
            id 唯一标识
            expression 切面表达式
         -->
        <aop:pointcut id="pointcut" expression="execution(public int com.mytest.proxy.springaoptest.xmlaspect.Calculator.*(int,int))"/>
        <!-- 配置切面及通知
            通过利用bean的实例代理器
         -->
        <aop:aspect ref="loggingAspect" order="2">
            <!-- 前置通知
                method 指定前置通知要调用的方法
                pointcut-ref 指定要应用的切面
             -->
            <aop:before method="beforeMethod" pointcut-ref="pointcut"></aop:before>
            <!-- 后置通知 -->
            <aop:after method="afterMethod" pointcut-ref="pointcut"></aop:after>
            <!-- 返回通知
                returning 返回的结果给那个参数
             -->
            <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"></aop:after-returning>
            <!-- 异常通知
                throwing 异常的结果给那个异常
             -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="ex"></aop:after-throwing>
        </aop:aspect>
        <aop:aspect ref="checkAspect" order="1">
            <aop:before method="checkargs" pointcut-ref="pointcut"></aop:before>
        </aop:aspect>

    </aop:config>

</beans>

四、事务

(一)使用JdbcTemplate

<!-- 利用外部配置文件 -->
<context:property-placeholder location="db.properties"></context:property-placeholder>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="user" value="${jdbc.user}"></property>
    <property name="password" value="${jdbc.password}"></property>
    <property name="driverClass" value="${jdbc.driverClass}"></property>
    <property name="jdbcUrl" value="${jdbc.jdbcurl}"></property>
</bean>

<!-- 配置Spring的 JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置NamedParameterJdbcTemplate对象,该对象可使用具名参数,其中没有无参数的构造器,所以必须为其构造器指定参数 -->
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
    <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>

public static void singleTest(){
    
    
    //更新数据测试
    String sql = "update usertable set name=?,phone=? where id=?";
    jdbcTemplate.update(sql,"wangwu","12345678901",20);
}

public static void batchTest(){
    
    
    //批量操作
    String sql2 = "insert into usertable(name,address,phone) values(?,?,?)";
    List<Object[]> users = new ArrayList<>();

    users.add(new Object[]{
    
    "aa","huadong","12345678901"});
    users.add(new Object[]{
    
    "bb","huanan","12345678902"});
    users.add(new Object[]{
    
    "cc","xinan","12345678903"});
    users.add(new Object[]{
    
    "dd","dongnan","12345678904"});

    jdbcTemplate.batchUpdate(sql2,users);
}

public static void getObject(){
    
    
    /*
        从数据库中获取一条记录,实际得到一个对应的对象
        注意:不是调用queryForObject( String sql, Class<T> requiredType, Object... args)方法!
        而需要调用 queryForObject( String sql,RowMapper<T> rowMapper, Object... args)
        1、其中RowMapper指定如何去映射结果集的行,常用的实现类为BeanPropertyRowMapper
        2、使用sql中列的别名完成列名和类的属性名的映射,例如name lastname
        3、不支持级联属性 JdbcTemplate 到底是一个jdbc的小工具,而不是ORM框架
     */
//        String sql = "select name lastname,address lastaddress,phone lastphone from usertable where id=?";
    String sql = "select name,address,phone from usertable where id=?";
    RowMapper<User> rowMapper = new BeanPropertyRowMapper<>(User.class);
    User user = jdbcTemplate.queryForObject(sql,rowMapper,18);

    System.out.println(user);
}

public static void getObjects(){
    
    
    //从数据库中获取多条记录,实际得到一个对应的对象的集合
    String sql = "select name,address,phone from usertable where id>?";
    RowMapper<User> rowMapper = new BeanPropertyRowMapper<>(User.class);
    List<User> users = jdbcTemplate.query(sql,rowMapper,20);

    System.out.println(users);
}

public static void columentQuery(){
    
    
    //获取单个列的值或做统计查询
    String sql = "select count(id) from usertable";
    long count = jdbcTemplate.queryForObject(sql,Long.class);
    System.out.println(count);
}

public static void namedParameterJdbcTemplateTest(){
    
    
    //可以为参数起名字
    /*
        好处:
        1、若有多个参数,则不用再去对应位置,直接对应参数名,便于维护
        缺点
        1、较为麻烦
     */
    String sql = "insert into usertable(name,address,phone) values(:na,:addr,:pho)";
    Map<String,Object> map = new HashMap<>();
    map.put("na","huanshi");
    map.put("addr","fujian");
    map.put("pho","09876543211");
    namedParameterJdbcTemplate.update(sql,map);
}

public static void namedParameterJdbcTemplateTest2(){
    
    
    //使用参数时,可以使用update(String sql ,SqlParameterSource parameterSource) 进行更新操作
    /*
        1、sql 语句的参数名和类的属性名一致
        2、使用SqlParameterSource的实现类BeanPropertySqlParameterSource作为惨呼
     */
    String sql = "insert into usertable(name,address,phone) values(:name,:address,:phone)";

    User user = new User();
    user.setName("laozhao");
    user.setAddress("zhangzhou");
    user.setPhone("09876543212");

    SqlParameterSource sqlParameterSource = new BeanPropertySqlParameterSource(user);

    namedParameterJdbcTemplate.update(sql,sqlParameterSource);
}

(二)事务

1、注解

/**
原子性(atomicity): 事务是一个原子操作, 由一系列动作组成. 事务的原子性确保动作要么全部完成要么完全不起作用.
一致性(consistency): 一旦所有事务动作完成, 事务就被提交. 数据和资源就处于一种满足业务规则的一致性状态中.
隔离性(isolation): 可能有许多事务会同时处理相同的数据, 因此每个事物都应该与其他事务隔离开来, 防止数据损坏.
持久性(durability): 一旦事务完成, 无论发生什么系统错误, 它的结果都不应该受到影响. 通常情况下, 事务的结果被写到持久化存储器中.
*/
@Transactional  //事务注解

2、事务注解启动

<!-- 配置事务注解 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 启动事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

3、事务的传播行为

//添加事务注解
//1、使用propagation 指定事务的传播行为,即当前事务方法被其他事务方法调用时
//如何使用事务,默认取值为REQUIRED,即使用调用方法的事务
//2、使用 isolation 指定事务的隔离级别最常用的取值为 READ_COMMITTED
//3、默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚,也可以对对应的属性进行设置。通常情况下去默认值即可
//4、使用 readOnly 指定事务是否为只读。表示这个事务只读数据但不更新数据,这样可以帮助数据库引擎优化事务若真的是一个只读数据库值的方法,应设置readOnly=true
//5、使用 timeout 指定可以强制回滚之前的事务可以占用的时间
//6、*rollback* 指定回滚规则(指定,那些异常可以不会滚)

@Transactional(propagation = Propagation.REQUIRES_NEW,
isolation = Isolation.READ_COMMITTED,
readOnly = false,
timeout = 2)
@Override
public void purchase(String isbn, String username) {
    
    
    //1、获取书的单价
    int price = bookShopDAO.findBookPriceByIsbn(isbn);

    //2、更新书的库存
    bookShopDAO.updateBookStock(isbn);

    //3、更新用户余额
    bookShopDAO.updateAccount(username,price);

}

4、Xml配置事务步骤

<!-- 1、配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 2、配置事务属性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- 配置要设置事务的方法
 			propagation 属性
                REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。 
                REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 
			isolation 属性
				隔离级别(DEFAULT(数据库默认的隔离级别),READ_UNCOMMITTED(读未提交),READ_COMMITTED(读已提交),REPEATABLE_READ(不可重复读),SERIALIZABLE(序列化))
			read-only 属性
				只读
		-->
        <tx:method name="purchase" propagation="REQUIRES_NEW" isolation="READ_COMMITTED" read-only="false"></tx:method>
        <tx:method name="checkout" read-only="false"></tx:method>
        <tx:method name="get*" read-only="true"></tx:method>
        <tx:method name="find*" read-only="true"></tx:method>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

<!-- 3、配置事务切入点,以及把事务切入点和事务属性关联起来 -->
<aop:config>
    <!-- 配置切入点 -->
    <aop:pointcut id="txPointCut" expression="execution(* com.spring.jdbc.txxml.service.impl.*.*(..))"/>
    <!-- 关联该切入点和事务属性的关联 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"></aop:advisor>
</aop:config>

猜你喜欢

转载自blog.csdn.net/magicproblem/article/details/108355452