Spring4.0笔记

2019-01-27  本文已影响41人  PC_Repair

Spring4.0

Spring是一个开源框架

Spring为简化企业级英语开发而生。使用Spring可以使简单的JavaBean实现以前只有EJB才能实现的功能

Spring是一个IOC(DI)和AOP容器框架

具体描述Spring:

image.png

示例:

<!--
    配置bean
    class: bean 的全类名,通过反射的方式在IOC容器中创建Bean,所以要求Bean中必须有无参数的构造器
    id: 标识容器中的bean,id 唯一
-->

<!-- 通过全类名的方式来配置bean -->
<bean id="helloWorld" class="com.ljf.spring.beans.HelloWorld">
    <property name="name2" value="Spring"> </property>
</bean>
// 1.创建Spring的IOC容器对象,在创建这个容器的时候会调用类的构造器对配置文件(xml)中的Bean初始化,同时调用set方法对属性就行赋值
// ApplicationContext 代表IOC容器
// ClassPathXmlApplicationContext:是ApplicationContext接口的实现类,该实现类从类路径下加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2.从IOC容器中获取Bean实例
// 利用id定位到IOC容器中的bean
HelloWorld helloworld = (HelloWorld) ctx.getBean("helloWorld");
// 利用类型返回IOC容器中的Bean,但要求IOC容器中必须只能有一个该类型的Bean
//HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
// 3.调用hello方法
helloWorld.hello();

IOC & DI概述

配置Bean

IOC和DI

IOC(Inversion of Control):其思想是反转资源获取的方向。传统的资源查找方式要求组件向容器发起请求查找资源。作为回应,容器适时的返回资源。而应用了IOC之后,则是容器主动地将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接受资源。这种行为也被称为查找的被动形式。

DI(Dependency Injection)--IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter方法)接受来自如容器的资源注入。相对于IOC而言,这种表述更直接。

<!--
    配置bean
    class: bean 的全类名,通过反射的方式在IOC容器中创建Bean,所以要求Bean中必须有无参数的构造器
    id: 标识容器中的bean,id 唯一
-->

Spring容器

ApplicationContext:

依赖注入的方式:Spring支持三种依赖注入的方式

<!-- 
    通过构造方法来配置bean的属性 
    使用构造器注入属性值可以指定参数的位置和参数的类型,以区分重载的构造器
-->

<bean id="car" class="com.ljf.spring.beans.Car">
    <constructor-arg value="Audi" index="0"></constructor-arg>
    <constructor-arg value="ShangHai" index="1"></constructor-arg>
    <constructor-arg value="300000" type="double"></constructor-arg>
</bean>

字面值:

引用其他Bean

<bean id="person" class="com.ljf.spring.beans.Person">
    <property name="name" value="Jack"> </property>
    <property age="age" value="24"> </property>
    <!-- 可以使用property的ref属性建立bean之间的引用联系  -->
    <property name="car" ref="car2"> </property>
    <!-- 内部bean -->
</bean>

XML配置里的Bean自动装配

继承Bean配置

依赖Bean配置

bean的作用域:singleton;prototype;WEB环境作用域

使用bean的 scope 属性来配置bean的作用域

使用外部属性文件

    <!-- 导入属性文件,配置数据库相关参数properties的属性:${url} -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- 数据库连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 使用外部化属性文件的属性 -->
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

Spring表达式语言:SpEL

IOC容器中的Bean的生命周期

配置Bean的后置处理器

<!--
    实现BeanPostProcessor接口,并具体提供
    Object postProcessBeforeInitialization(Object bean, String beanName): init-method 之前被调用
    Object postProcessAfterInitialization(Object bean, String beanName): init-method 之后被调用
    
    bean: bean 实例本身
    beanName: IOC 容器配置的bean的名字
    返回值:是实际上返回给用户的那个Bean,注意:可以在以上两个方法中修改返回的bean,甚至返回一个新的bean
-->

<!-- 配置bean的后置处理器:不需要配置id,IOC容器自动识别是一个BeanPostProcessor -->
<bean class="com.ljf.MyBeanPostProcessor"></bean>

通过静态工厂方法来配置 bean

public class StaticCarFactory {
    private static Map<String, Car> cars = new HashMap<String, Car>();
    static {
        cars.put("audi", new Car("audi", 30000));
        cars.put("ford", new Car("ford"m 40000));
    }
    // 静态工厂方法
    public static Car getCar(String name) {
        return cars.get(name);
    }
}
<!-- 通过静态工厂方法来配置 bean。注意补水配置静态工厂方法实例,而是配置bean实例 -->
<!-- 
    class 属性:指向静态工厂方法的全类名
    factory-method: 指向静态工厂方法的名字
    constructor-arg: 如果工厂方法需要传入参数,则使用constructor-arg 来配置参数
-->
<bean id="car1"
    class="com.ljf.beans.StaticCarFactory"
    factory-method="getCar">
    <constructor-arg value="audi"></constructor-arg>
</bean>

通过实例工厂方法配置bean

FactoryBean

<!--
    通过FactoryBean 来配置Bean的实例
    class: 指向FactoryBean的全类名
    property: 配置FactoryBean的getObject() 方法返回的实例

    但实际返回的实例是 FactoryBean的getObject() 方法返回的实例
-->
<bean id="car" class="com.ljf.bean.factorybean.CarFactoryBean">
    <property name="brand" value="BMW"></property>
</bean>
public class CarfactoyBean implements FactoryBean<Car> {
    ...
    Override 3 个方法
}

基于注解的方式

在classpath中扫描组件

组件装配

使用@Autowired自动装配Bean

@Resource 注解要求提供一个Bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为Bean的名称

@Inject 和 @Autowired 注解一样也是按类型匹配注入的Bean,但没有required属性

Spring 4.x新特性:泛型依赖注入

AOP

问题:

public class ArithmeticCalculatorLoggingProxy {
    // 要代理的对象
    private ArithmeticCalculator target;
    private ArithmeticCalculator getLoggingProxy() {
        ArithmeticCalculator proxy = null;
        
        // 代理对象由哪一个类加载器负责加载
        ClassLoader loader = target.getClass().getClassLoader();
        // 代理对象的类型,即其中有哪些方法
        Class[] interfaces = new Class[]{ArithmeticCalculator.class};
        // 当调用代理对象其中的方法时,该执行的代码
        InvocationHandler h = new InvocationHandler() {
            /**
             * proxy: 正在返回的那个代理对象,一般情况下,在invoke方法中都不使用该对象
             * method: 正在被调用的方法
             * args: 调用方法时,传入的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String methodName = method.getName();
                // 日志
                System.out.println("ATGUIGU-> The method " + methodName + " bengins with " + Arrays.asList(args));
                // 执行方法
                Object result = method.invoke(target, args);
                // 日志
                return result;
            }
        };
        proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
        return proxy;
    }
}

AOP术语:

在Spring中启用AspectJ注解支持

步骤:

1)加入jar包

2)在配置文件中加入aop命名空间,配置自动扫描包

3)基于注解的方式

<context:component-scan base-package="com.ljf.spring.aop.impl"> </context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@Aspect
@Component
public class LoggingAspect {
    // 声明该方法是一个前置通知:在目标方法开始之前执行
    @Before("execution(* com.ljf.spring.aop.impl.*.*(int, int))")
    public void beforeMethod(JoinPoint joinPoint) {
        String methodName = JoinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(JoinPoint.getArgs());
        System.out.println("The method " + methodName + " begins with " + args);
    }
    
    // 后置通知:在目标方法执行后(无论是否发生异常),执行通知
    // 在后置通知中还不能访问目标方法执行的结果
    @After("execution(* com.ljf.spring.aop.impl.*.*(int, int))")
    public void beforeMethod(JoinPoint joinPoint) {
        String methodName = JoinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends.");
    }
    
    // 在方法正常结束后执行的代码
    // 返回通知是可以访问到方法的返回值
    @AfterReturning(value="execution(* com.ljf.spring.aop.impl.*.*(..))", returning="result")
    public void AfterReturning(JoinPoint joinPoint, Object result) {
        String methodName = JoinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends with." + result);
    }
    
    // 在目标方法出现异常时执行的代码
    // 可以访问到异常对象;且可以指定在出现特定异常时再执行通知代码
    @AfterThrowing(value="execution(* com.ljf.spring.aop.impl.*.*(..))", throwing="ex")
    public void AfterThrowing(JoinPoint joinPoint, Exception ex) {
        String methodName = JoinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " occurs excetion: " + ex);
    }
    
    // 环绕通知需要携带 ProceedingJoinPoint 类型的参数
    // 环绕通知类似于动态代理的全过程:ProceedingJoinPoint 类型的参数可以决定是否执行目标方法
    // 且环绕通知必须由返回值,返回值即为目标方法的返回值
    @Around(value="execution(* com.ljf.spring.aop.impl.*.*(..))")
    public void AroundMethod(ProceedingJoinPoint pjd, Exception ex) {
        System.out.println("aroundMethod");
        return 100;
    }
}

@Order(x)指定切面的优先级,x为值,值越小,优先级越高

重用切点表达式

@Aspect
@Component
public class LoggingAspect {
    @Pointcut("execution(* com.ljf.spring.aop.impl.*.*(..))"")
    public void declareJointPointExpression(){}
    
    // 声明该方法是一个前置通知:在目标方法开始之前执行
    @Before("declareJointPointExpression()")
    public void beforeMethod(JoinPoint joinPoint) {
        String methodName = JoinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(JoinPoint.getArgs());
        System.out.println("The method " + methodName + " begins with " + args);
    }
    
    // 后置通知:在目标方法执行后(无论是否发生异常),执行通知
    // 在后置通知中还不能访问目标方法执行的结果
    @After("declareJointPointExpression()")
    public void beforeMethod(JoinPoint joinPoint) {
        String methodName = JoinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends.");
    }
    ...
}

基于配置文件的AOP配置

<!-- 配置切面的bean -->
<bean id="LoggingAspect"
      class="com.ljf.spring.aop.xml.LoggingAspect"></bean>
<!-- 配置AOP -->
<aop:config>
    <!-- 配置切点表达式 -->
    <aop:pointcut expression="execution(* com.ljf.spring.aop.impl.*.*(..))" id="pointcut"/>
    <!-- 配置切面及通知 -->
    <aop:aspect ref="LoggingAspect" order="2">
        <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
        <aop:after method="afterMethod" pointcut-ref="pointcut"/>
        <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
        <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
    </aop:aspect>
    <aop:aspect ref="vlidationAspect" order="1">
        <aop:around method="aroundMethod" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>

JDBCTemplate

JDBCTemplate 简介:

    <!-- 配置数据库相关参数properties的属性:${url} -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- 数据库连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>
        <property name="minPoolSize" value="${c3p0.minPoolSize}"/>
        <property name="autoCommitOnClose" value="${c3p0.autoCommitOnClose}"/>
        <property name="checkoutTimeout" value="${c3p0.checkoutTimeout}"/>
        <property name="acquireRetryAttempts" value="${c3p0.acquireRetryAttempts}"/>
    </bean>

    <!-- 配置 Spring 的 JdbcTemplate -->
    <bean id="jdbcTemplate"
          class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
public class JDBCTest {
    private ApplicationContext ctx = null;
    private JdbcTemplate jdbcTemplate;
    
    {
        ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");
    }
    
    /**
     * 调用queryForObject(String sql, RowMapper<Employee> rowMapper, Object... args)
     * 1.其中的RowMapper指定如何去映射结果集的行,常用的实例类为BeanPropertyRowMapper
     * 2.使用SQL中列的别名完成列名和类的属性名的映射,例如 last_name  lastName
     * 3.不支持级联属性(如department.id),JdbcTemplate 到底是一个JDBC的小工具,而不是ORM框架
     */
    @Test  // 查找一个对象
    public void testQueryForObject() {
        String sql = "SELECT id, last_name lastName, email, dept_id as \"department.id\" FROM employees WHERE id = ?";
        RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
        Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 1);
        System.out.println(employee);
    }
    
    /**
     * 查找实体类的集合
     */
    @Test  // 查找一组对象
    public void testQueryForList() {
        String sql = "SELECT id, last_name lastName, email FROM employees WHERE id > ?";
        RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
        List<Employee> employees = jdbcTemplate.query(sql, rowMapper, 5);
        System.out.println(employees);
    }
    
    /**
     * 获取单个列的值,或做统计查询
     */
    @Test
    public void testQueryForObject2() {
        String sql = "SELECT count(id) FROM employees";
        long count = jdbcTemplate.queryForObject(sql, Long.class);
        System.out.println(count);
    }
    
    
    /**
     * 批量执行 INSERT, UPDATE, DELETE
     */
    @Test
    public void testBatchUpdate() {
        String sql = "INSERT INTO employees(last_name, email, dept_id) VALUES(?,?,?)";
        List<Object[]> batchArgs = new ArrayList<>();
        batchArgs.add(new Object[]{"AA", "aa@163.com", 1});
        batchArgs.add(new Object[]{"BB", "bb@163.com", 2});
        batchArgs.add(new Object[]{"CC", "cc@163.com", 3});
        batchArgs.add(new Object[]{"DD", "dd@163.com", 3});
        batchArgs.add(new Object[]{"EE", "ee@163.com", 2});
        jdbcTemplate.batchUpdate(sql, batchArgs);
    }
}

Spring中的事务管理

事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性。

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

<!-- 启用事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

在对应方法上加上@Transactional注解

REQUIRED传播行为

REQUIRES_NEW传播行为

并发事务所导致的问题:

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

使用xml配置Spring事务

<!-- 1.配置事务管理器 -->
<bean id="transactionManager"
      class="rg.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据库连接池 -->
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 2.配置事务属性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- 传播行为 -->
        <tx:method name="save*" propagation="REQUIRED" />
        <tx:method name="insert*" propagation="REQUIRED" />
        <tx:method name="add*" propagation="REQUIRED" />
        <tx:method name="create*" propagation="REQUIRED" />
        <tx:method name="delete*" propagation="REQUIRED" />
        <tx:method name="update*" propagation="REQUIRED" />
        <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
        <tx:method name="select*" propagation="SUPPORTS" read-only="true" />
        <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
    </tx:attributes>
</tx:advice>

<!-- 3.配置事务切入点,以及把事务切入点和事务属性关联起来 -->
<aop:config>
    <aop:advisor advice-ref="txAdvice"
                 pointcut="execution(* com.bank.service.*.*(..))" />
</aop:config>
上一篇下一篇

猜你喜欢

热点阅读