Spring学习笔记-使用-2

注解注入

1、创建bean

(1)在创建IOC容器时,Spring 能够从 classpath 下自动扫描, 侦测和实例化具有特定注解的组件

特定注解包括:@Component(基本注解)   @Repository(标识持久层) @Service(标识服务层)  @Cotroller(标识控制层)

这四个注解对Spring而言没有什么区别。

在创建bean时,默认采用类名第一个字母小写作为bean的id。也可以通过注解的value属性制定bean的id。

(2)需要使用context:component-scanbase-package关键字指定需要扫描的包,因此需要引入context命名空间。Spring会扫描该包以及其子包,如果需要扫描多个包,则用逗号分隔。

(3)通过resource-pattern来过滤指定的类

(4)使用context:include-filtercontext:exclude-filter子节点来过滤指定的类。

此时需要制定type和expression关键字。

type常用的有两种,annotation(所有标注了expression指定的注解的类都不扫描或者扫描)

                                assinable(所有继承或扩展了expression指定的类的类都不扫描或者扫描)

注意:context:include-filter需要先将use-default-filters关键字指定为false才能实现 只扫描指定的类。

<context:component-scan base-package="com.robin.spring"
                            resource-pattern="annotation/*.class"></context:component-scan>
    <context:component-scan base-package="com.robin.spring">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
        <context:exclude-filter type="assignable" expression="com.robin.spring.controller.UserController"></context:exclude-filter>
    </context:component-scan>

    <context:component-scan base-package="com.robin.spring" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter>
    </context:component-scan>

2、使用bean

通过@Autowired、@Resource、@Inject来注入bean。注解可添加到构造器、变量、方法上(很多时候还会添加在形参的前面

@Autowired,先按照类型匹配,如果同一类型有多个bean,则按照名称匹配。有一个required属性,若为true,则没有对应的bean会报错,如果为false,没有对应的bean则直接给null,默认为true。

@Resource,直接按照名称匹配,没有required属性。

@Inject,和Autowired一样,但是没有required属性。

注意:Autowired可以用于集合之上,则spring会扫描所有匹配的bean加入集合。如果为Map,且key为String,则会将bean的id作为key将bean作为value添加到Map里。

3、泛型依赖注入

Spring4之前并不支持泛型依赖注入,那么BaseRepository<User>和BaseRepository<Role>将会被认为是同一种类型的bean

Spring4之后支持泛型依赖注入,那么BaseRepository<User>和BaseRepository<Role>将会被认为是不同类型的bean

 

动态代理

实现Hook的一种方式,需要调用Proxy.newProxyInstance(classLoader,interfaces,h)方法来创建代理对象,classLoader是被代理对象的类加载器,interfaces是被代理对象对应的接口类的所有接口方法集合,h是处理函数。此方法内部实现是:创建一个新的被代理对象对应接口的实现类,类中的每个接口的实现均调用传入的h方法。然后实例化一个新类的对象,并返回。

public class ArithmeticCalculatorProxy {
    private ArithmeticCalculator target;

    public ArithmeticCalculatorProxy(ArithmeticCalculator arithmeticCalculator) {
        target = arithmeticCalculator;
    }

    public ArithmeticCalculator getCalculator() {
        ArithmeticCalculator proxy = null;

        ClassLoader classLoader = target.getClass().getClassLoader();
        Class[] interfaces = target.getClass().getInterfaces();
        InvocationHandler h = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                System.out.println("Before invoke " + method.getName());
                Object result = method.invoke(target,args);
                System.out.println("After invoke " + method.getName());
                return result;
            }
        };
        proxy = (ArithmeticCalculator) Proxy.newProxyInstance(classLoader,interfaces,h);
        return proxy;
    }
}

AOP(spring内部用动态代理实现)

1、一些术语

切面(Aspect):  横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象

通知(Advice):  切面必须要完成的工作

目标(Target): 被通知的对象

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

连接点(Joinpoint):程序执行的某个特定位置

切点(pointcut):用于查询连接点

2、实现

(1)引入maven仓库:spring-aop、aspectjrt、aspectjweaver。

(2)在配置文件中引入aop和context命名空间,配置context:component-scan和aop:aspectj-autoproxy

(3)将切面抽象成一个类,并添加@Component和@Aspect注解。

(4)在切面类中添加处理方法,并在方法上加上 通知注解 来标明方法相对于 目标 的执行位置。

(5)如果处理方法需要使用连接点的详细信息时,可以在方法中加入JoinPoint形参来获取

注:通知注解有:

@Before(前置处理)

@After(后置处理,目标执行结束或者抛异常后都会执行,但是不能访问目标的执行结果)、

@AfterReturning(返回通知)

@AfterThrowing(异常通知)

@Around(环绕通知,拦截方法的执行,一定要有返回值)

在注解内需要指明目标,@Before("execute(返回值 接口名.方法名(形参列表))")

try {
    //前置通知
    method.invoke();  //环绕通知会在此拦截方法的执行,直接将此行替换成 环绕通知
    //返回通知
}catch (Exception e) {
    //异常通知
}
//后置通知

注:当同一个方法有多个关联的切面时,切面之间的执行顺序可以通过@Order(num)来指定,num值越小优先级越高

3、为了使通知表达式可重用,可以利用@Pointcut声明切点

@Order(1)
@Aspect
@Component
public class LoggingAspect {
    @Pointcut("execution(double com.robin.spring.aop.DataService.*(*,*))")
    public void declarationPointCut() {
    }

    @Before("com.robin.spring.aop.LoggingAspect.declarationPointCut()")
    public void beforeMethod(JoinPoint joinPoint) {
        System.out.println("Before Method " + joinPoint.getSignature().getName() + " with param " + Arrays.asList(joinPoint.getArgs()));
    }

    @After("com.robin.spring.aop.LoggingAspect.declarationPointCut()")
    public void afterMethod(JoinPoint joinPoint) {
        System.out.println("After method " + joinPoint.getSignature().getName());
    }

    @AfterThrowing(value = "com.robin.spring.aop.LoggingAspect.declarationPointCut()", throwing = "exception")
    public void afterMethodThrowing(Exception exception) {
        System.out.println("Throw exception " + exception.getMessage());
    }

    @AfterReturning(value = "com.robin.spring.aop.LoggingAspect.declarationPointCut()",returning = "result")
    public void afterMethodReturn(JoinPoint joinPoint, Object result) {
        System.out.println("Method return " +  result);
    }

    @Around("com.robin.spring.aop.LoggingAspect.declarationPointCut()")
    public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint) {
        Object result = null;

        Object param[] = proceedingJoinPoint.getArgs();
        if ((Integer)param[1] == 0) {
            param[1] = 1;
        }

        try {
            result = proceedingJoinPoint.proceed(param);
        } catch (Throwable e) {
            System.out.println(e);
        }
        return result;
    }
}

4、基于配置文件的aop

先生成服务和切面的bean,然后配置aop,在配置aop时,先生成切点的bean,然后再配置通知

<bean id="dataService" class="com.robin.spring.config.DataServiceImpl"></bean>
<bean id="aspect" class="com.robin.spring.config.LoggingAspect"></bean>
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(double com.robin.spring.config.DataService.*(*,*))"/>
    <aop:aspect ref="aspect" order="1">
        <aop:before method="beforeMethod" pointcut-ref="pointCut"></aop:before>
        <aop:after method="afterMethod" pointcut-ref="pointCut"></aop:after>
        <aop:after-throwing method="afterMethodThrowing" pointcut-ref="pointCut" throwing="exception"></aop:after-throwing>
        <aop:after-returning method="afterMethodReturn" pointcut-ref="pointCut" returning="result"></aop:after-returning>
        <aop:around method="aroundMethod" pointcut-ref="pointCut"></aop:around>
    </aop:aspect>
</aop:config>

 

JDBC(jdbcTemplate / namedPrameterJdbcTemplate)

1、maven依赖

        引入spring-jdbc、mysql-connector-java、c3p0的maven依赖

2、配置

         定义一个C3P0数据源bean,然后定义一个jdbcTemplate或者namedPrameterJdbcTemplate的bean。

<context:property-placeholder location="db.properties"></context:property-placeholder>
    <!--连接c3p0数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="jdbcNamedTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg ref="dataSource"></constructor-arg>
    </bean>

注意,定义数据源时,使用了属性文件,其中

jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/robin_practice

url规则:jdbc:mysql://(host): 端口(MySql的默认端口是3306) / 数据库名

 

3、使用

        使用时调用jdbcTemplate.update(),query(),queryForObject()等来进行增删改查。

@Test
    public void testNameJdbc() {
        String sql = "INSERT INTO employees(id,name,DEPT_ID) VALUES(:id,:name,:deptId)";
        EmployeeDao employeeDao = new EmployeeDao();
        employeeDao.setDeptId(6);
        employeeDao.setId(6L);
        employeeDao.setName("cc");
        SqlParameterSource paramSource = new BeanPropertySqlParameterSource(employeeDao);
        namedParameterJdbcTemplate.update(sql,paramSource);
    }


    @Test
    public void testUpdate() {
        String sql = "UPDATE employees SET name = ? WHERE id = ?";
        jdbcTemplate.update(sql,"robin",1);
    }

    @Test
    public void batchInsert() {
        String sql = "INSERT INTO employees(id, name, DEPT_ID) VALUES(?,?,?)";
        List<Object[]> batchArgs = new ArrayList<Object[]>();
        batchArgs.add(new Object[]{4,"aa",4});
        batchArgs.add(new Object[]{5,"bb",5});
        jdbcTemplate.batchUpdate(sql,batchArgs);
    }

    @Test
    public void testSelect() {
        String sql = "SELECT id,name,DEPT_ID deptId FROM employees WHERE id = ?";
        RowMapper<EmployeeDao> rowMapper = new BeanPropertyRowMapper<EmployeeDao>(EmployeeDao.class);
        EmployeeDao employeeDao = jdbcTemplate.queryForObject(sql,rowMapper,4);
        System.out.println(employeeDao);
    }
}

4、NameParameterJdbcTemplate

         NameParameterJdbcTemplate是具名的JdbcTemplate,其sql语句的变量不是用?表示而是用变量名表示,然后调用update等方法时,传入Map<String,Object>或者SqlParameterSource。

 

事务

1、定义

            保证多个数据库操作的原子性,如果某一个操作失败,则回滚。

2、配置

           需要先定义一个transactionManager的bean,然后再使用tx:annotation-driven关键字让注解起效。

    <context:property-placeholder location="db.properties"></context:property-placeholder>
    <context:component-scan base-package="com.robin.tx"></context:component-scan>

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

    <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、使用

           在需要用事务的方法上添加事务注解@Transactional就可以实现事务了。

4、事务的传播行为

         使用propagation属性指定事务的传播行为,默认为REQUIRED(只要有事务,则以最外层事务为准)还可以指定为REQUIRES_NEW(以内部事务为准,进入内部事务时外部事务失效),需要添加在内部事务上。

注:同时买多本书时,整个买操作为外部事务,买单本书为内部事务。required是指一旦有异常,则回滚到原始状态,required_new则表示哪本书有异常哪本书回滚。

例子:@Transactional(propagation = Propagation.REQUIRES_NEW)

5、事务的隔离级别

       使用isolation属性制定隔离级别

6、指定异常回滚

        默认对所有运行时异常回滚。使用rollbackFor、noRollbackFor、rollbackForClassName、noRollbackForClassName属性设置指定异常的回滚。一般默认就行。

7、优化

      可以指定readOnly属性来帮助数据库引擎优化事务。

      使用timeOut指定强制回滚之前事务占用时间(如果事务占用时间超过指定时间,则强制回滚)

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值