spring开发

一 spring的核心原理

spring框架的一大目的是为了解耦合,为了达到这个目标,spring的方案是在底层使用对象工厂,即容器,把需要管理的对象添加进来(常是单例),当需要使用这些对象的时候,不需要重新创建,由容器提供已经建立好的对象,直接使用即可。这样,把对象集中统一的进行管理,降低了耦合度,也支持了其他特性的实现。

二核心功能解析与实现

1 IOC 控制反转

把原有自己掌握的东西交由别人控制,把原本自己负责的对象建立与管理,交由spring框架进行建立和控制。

因此,在实现IOC的时候,要对类文件进行一个标注,代表着这个文件里的类,对象将要被spring框架建立与管理。

<bean id="accountController" class="com.example.controller.AccountController">
        <property name="accountService" ref="accountService"></property>
    </bean>

 

在这里,bean表示这是spring需要管理的springbean,即运行时会根据这个标签的标注建立和管理对象,id是bean的标识符,在被其他bean调用时使用,class是bean所标记的类文件,也可以直接使用注释的方法进行IOC,

<context:component-scan base-package="com.example"></context:component-scan>

这个标签的意思是将对标识的包进行扫描,包里类文件已经有注释的会被标记为被spring管理。

@Controller(“accountController”)
public class AccountController {

    @Autowired
    private AccountService accountService;

    public void transfer(String fromName,String toName,Double money) {
        try {
            accountService.transfer(fromName, toName, money);
            System.out.println("success");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("fail");
        }
    }
}
@Controller就是一个注释,类名上有这个注释的将被spring管理,accountController即是id,如果不指定value值, 那么默认的id值就是类名的名字, 第一个字母小写.

@Component,
@Controller @Service @Repository都可以达到这个目的,其中
@Controller               :修饰WEB层类 --->web | SpringMVC
@Service                    :修饰业务层类 --->service
@Repository             :修饰DAO层类 --->dao
2 DI 依赖注入

如果我们托管的某一个类中存在属性,需要spring在创建该类实例的时候,顺便给这个对象里面的属性进行赋值。 这就是依赖注入。
<bean id="accountController" class="com.example.controller.AccountController">

       <!--使用有参构造进行属性的注入,使用<constructor-arg>标签注入-->

      <constructor-arg name="userService" ref="userService"></constructor-arg>
      <!--set方法注入-->
      <property name="accountService" ref="accountService"></property>
      <property name="age" value="28"></property> </bean>

其中,使用xml文件的依赖注入如上,使用注解开发的如下。使用@Value,@Autowired,@Qualifier,@Resource

@Component("accountService")
public class AccountServiceImpl implements AccountService {

  
@Value("奥巴马")
  
  private String name;  
    @Autowired//当有多个AccountDao实现类时候, @Autowired会在在Spring容器里面找id为accountDao的对象注入,找不到就报错
    private AccountDao accountDao;

  

Autowired: 自动装配, 如果spring的核心容器中,只有一个该类型的对象,则自动把那个对象注入给当前属性;
* 如果spring的核心容器中,不止有一个该类型的对象,那么就会根据属性名匹配对象的id,匹配上哪个就注入哪个;
* 如果一个都匹配不上,那么我们还可以通过Qualifier指定要注入的对象的id


    @Autowired
    @Qualifier("userServiceImplAnother")
    private UserService userService;

 

    //能够进行自动装配以及手动装配

 

    @Resource(name = "userServiceImpl")
    private UserService userService;




@Override
public void save() { System.out.println("AccountServiceImpl---save()"); accountDao.save(); } }

 

 

 

3 AOP

面向切面编程。在不修改源码的基础上,对我们的已有方法进行增强。说白了就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,进行增强

AOP中的术语

  • JoinPoint: 连接点(所有可以被增强的方法)

    类里面哪些方法可以被增强,这些方法称为连接点. 在spring的AOP中,指的是业务层的类的所有现有的方法。

  • Pointcut: 切入点(具体项目中真正已经被增强的方法)

    在类里面可以有很多方法被增强,但是实际开发中,我们只对具体的某几个方法而已,那么这些实际增强的方法就称之为切入点

  • Advice: 通知/增强 (具体用于增强方法的代码)

    增强的逻辑、称为增强,比如给某个切入点(方法) 扩展了校验权限的功能,那么这个校验权限即可称之为增强 或者是通知

    通知分为:

    前置通知: 在原来方法之前执行.

    后置通知: 在原来方法之后执行. 特点: 可以得到被增强方法的返回值

    异常通知: 目标方法出现异常执行. 如果方法没有异常,不会执行. 特点:可以获得异常的信息

    最终通知: 指的是无论是否有异常,总是被执行的。

    环绕通知:在方法之前和方法之后执行. 特点:可以阻止目标方法执行

  • Aspect: 切面(所有的通知都是在切面中的)

  • 配置aop要先告诉spring
  •  

    基于xml的aop配置

  • <!--全部采用纯配置文件方式-->
        <!--1. 对UserServiceImpl进行IOC配置-->
        <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>
        <!--2. 对切面类PermissionAspect进行IOC配置-->
        <bean id="permissionAspect" class="com.itheima.aspect.PermissionAspect"></bean>
        <!--
            3. 配置AOP <aop:config>
               1. 配置切入点 <aop:pointcut id="" expression=""/>
                  id: 切入点的唯一标识
                  expression: 切入点表达式
               2. 配置通知
               3. 配置切面关联通知和切入点
        -->
        <aop:config>
            <!--配置切入点-->
            <aop:pointcut id="pt1"
                          expression="execution(* com.itheima.service.impl.UserServiceImpl.*(..))"/>
    
            <aop:pointcut id="pt2" expression="execution(* com.itheima.service.impl.UserServiceImpl.update(..))"/>
            <!--配置切面-->
            <aop:aspect id="aspect1" ref="permissionAspect">
                <!--配置前置通知-->
                <aop:before method="checkPermission" pointcut-ref="pt1"></aop:before>
    
                <!--配置后置通知-->
                <aop:after-returning method="printLog" pointcut-ref="pt1"></aop:after-returning>
    
                <!--配置异常通知-->
                <aop:after-throwing method="showException" pointcut-ref="pt1"></aop:after-throwing>
    
                <!--配置最终通知-->
                <aop:after method="showEnd" pointcut-ref="pt1"></aop:after>
    
                <!--配置环绕通知-->
                <aop:around method="showMillions" pointcut-ref="pt2"></aop:around>
            </aop:aspect>
        </aop:config>
    </beans>

    配置aop要先将切入点和切面进行ioc配置,在进行aop的配置

  • 基于注解的配置
 */
@Service
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("执行添加...");
        int num = 10/0;
    }
}
 * 1. 注解方式配置切入点 在方法上添加Pointcut注解配置切入点,方法的名字就是切入点的唯一标识
 * 2. 配置通知
 *    1. 前置通知 Before注解
 *    2. 后置通知 AfterReturning注解
 *    3. 异常通知 AfterThrowing注解
 *    4. 最终通知 After注解
 *    5. 环绕通知 Around注解
 * 3. 配置切面:在切面类上添加Aspect注解
 */
@Component
@Aspect
public class PermissionAspect {
    @Pointcut("execution(* com.itheima.service.impl.UserServiceImpl.*(..))")
    public void pt1(){

    }

    @Pointcut("execution(* com.itheima.service.impl.UserServiceImpl.update(..))")
    public void pt2(){

    }

    @Before("pt1()")
    public void checkPermission(){
        System.out.println("校验权限...");
    }

    @AfterReturning("pt1()")
    public void printLog(){
        System.out.println("打印日志...");
    }

    @AfterThrowing("pt1()")
    public void showException(){
        System.out.println("出现异常了,请检查...");
    }

    @After("pt1()")
    public void showEnd(){
        System.out.println("执行完毕...");
    }

    //计算目标方法的执行时长
    @Around("pt2()")
    public void showMillions(ProceedingJoinPoint joinPoint){
        //1. 记录当前时间
        long timeMillis1 = System.currentTimeMillis();
        //2. 执行目标方法
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        //3. 再记录当前时间
        long timeMillis2 = System.currentTimeMillis();
        //4. 将两次的时间相减,得到方法的执行时长
        System.out.println(timeMillis2 - timeMillis1);
    }
}
    <!--1. 包扫描-->
    <context:component-scan base-package="com.itheima"/>

    <!--
        2. 加载aop的注解驱动
    -->
    <aop:aspectj-autoproxy />

在aspect类中注解切入点,切入面,切入方式

4事务管理(声明式)

声明式的事务管理的思想就是AOP的思想。面向切面的方式完成事务的管理。声明式事务有两种,==xml配置方式和注解方式.==

1、xml

配置事务管理器

<bean id="transactionManager"         class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
</bean>

 

配置事务建议(事务的规则)

<!--配置事务规则-->
<tx:advice id="transferAdvice" transaction-manager="transactionManager">
    <!--配置事务的属性-->
    <tx:attributes>
        <!--
                哪个方法需要使用什么样的事务配置
                    rollback-for="java.lang.Exception" 什么时候回滚:遇到所有的Exception都会回滚
                    no-rollback-for="java.lang.NullPointerException" 什么时候不回滚:遇到NullPointerException不回滚
                    timeout="-1" 事务的超时时间,默认不超时
                    read-only="false" 是否是只读事务,默认不是只读事务,只读事务只会针对于查询方法,如果是增删改一定不能设置为只读
                    isolation="" 事务的隔离级别: 目的是为了防止不同事务之间相互影响
                        1. Read Uncommitted  读取到未提交的事务,在这种隔离级别下,可能发生脏读、不可重复读、幻读
                        2. Read Committed(Oracle的默认隔离级别) 读取到已提交的事务,在这种隔离级别下,不可能发生脏读,但是可能发生不可重复读和幻读
                        3. Repeatable Read(mysql的默认隔离级别) 可重复读,在这种隔离级别下不会发生脏读、不可重复读,但是有可能发生幻读
                        4. Serializable 串行化的,在这种隔离级别下不会发生脏读、不可重复读、幻读

                    propagation="" 事务的传播行为
                            - PROPAGATION_REQUIRED:默认值,也是最常用的场景.
                              如果当前没有事务,就新建一个事务,
                              如果已经存在一个事务中,加入到这个事务中。

                            - PROPAGATION_SUPPORTS:
                              如果当前没有事务,就以非事务方式执行。
                              如果已经存在一个事务中,加入到这个事务中。

                            - PROPAGATION_MANDATORY
                              如果当前没有有事务,就抛出异常;
                              如果已经存在一个事务中,加入到这个事务中。

                            保证不在同一个事务里:
                            - PROPAGATION_REQUIRES_NEW
                              如果当前有事务,把当前事务挂起,创建新的事务但独自执行

                            - PROPAGATION_NOT_SUPPORTED
                              如果当前存在事务,就把当前事务挂起。不创建事务

                            - PROPAGATION_NEVER
                              如果当前存在事务,抛出异常

            -->
        <tx:method name="transfer"
                   rollback-for="java.lang.Exception"
                   no-rollback-for="java.lang.NullPointerException"/>
    </tx:attributes>
</tx:advice>

 

配置事务的AOP

<aop:config>
    <!--声明切入点-->
    <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.AccountServiceImpl.transfer(..))"/>
    <!--绑定切入点和事务-->
    <aop:advisor advice-ref="transferAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>

 

2.注解(推荐)

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager"/>

 

@Transactional(rollbackFor = Exception.class,noRollbackFor = NullPointerException.class)
@Service
public class AccountServiceIMpl implements AccountService {
    @Autowired
    public AccountDao accountDao;


    @Override
    public void transfer(String fromName, String toName, Double money) {

        accountDao.updateAccount(new Account(null, fromName, -money));
//        Account account =null;
//        System.out.println(account.getName());

//        int num = 10/0;

        accountDao.updateAccount(new Account(null, toName, money));

    }
}

三 其他内容

1spring整合mybatis

  1. 引入mybatis以及整合的相关依赖

  2. 在spring的IOC容器中创建DataSource对象(可以使用spring内置的DataSource)

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="username" value="root"></property>
            <property name="password" value="123"></property>
            <property name="url" value="jdbc:mysql:///day20?characterEncoding=utf8"></property>
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        </bean>

     

  3. 在spring的IOC容器中创建SqlSessionFactoryBean对象,并且可以指定要配置别名的包或者是加载核心配置文件

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"></property>
            <!--加载mybatis的主配置文件-->
            <!--<property name="configLocation" value="classpath:SqlMapConfig.xml"></property>-->
            <!--别名配置的包扫描-->
            <property name="typeAliasesPackage" value="com.itheima.pojo"></property>
        </bean>

     

  4. 在spring的IOC容器中创建MapperScannerConfigurer对象,用于扫描Dao接口创建代理对象

    <bean id="scannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.itheima.dao"></property>
        </bean>

     

  5. 通过注解的方式注入Dao代理对象

2纯注解方式进行配置

@Configuration
@ComponentScan(basePackages = "com.itheima")
public class SpringConfig {
    @Bean
    public DataSource getDataSource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("123");
        dataSource.setUrl("jdbc:mysql:///day20?characterEncoding=utf8");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return dataSource;
    }

    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        //设置dataSource属性
        sqlSessionFactoryBean.setDataSource(dataSource);
        //设置typeAliasesPackage
        sqlSessionFactoryBean.setTypeAliasesPackage("com.itheima.pojo");
        return sqlSessionFactoryBean;
    }

    @Bean
    public MapperScannerConfigurer getMapperScannerConfigurer(){
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("com.itheima.dao");
        return mapperScannerConfigurer;
    }
}

3@import import标签

用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration注解, 导入其他配置文件

@Configuration
@ComponentScan("com.itheima")
@Import({MybatisConfig.class})
public class SpringConfign {


}
<import resource="classpath:application-mybatis.xml"/>

4@PropertySource  context:property-placeholder标签

引入外部的properties属性文件

 

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_day02
jdbc.username=root
jdbc.password=123456

  

@PropertySource(value = {"classpath:jdbc.properties"})
public class MybatisConfig {
    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.driver}")
    private String driver;
}
<context:property-placeholder location="classpath:jdbc.properties"/>

 5  Spring整合测试

导入spring整合Junit的坐标,在测试类上面标记注解

/**
 * 包名:com.itheima.test
 *
 * @author Leevi
 * 日期2020-08-09  15:16
 * 直接在测试用例中,注入要使用的对象AccountController
 * 1. 我们自己不创建核心容器,那么我们就应该交给别人(Junit)去创建核心容器
 *    1. 引入spring整合Junit的依赖  spring-test
 *    2. 保证Junit的版本是4.12以及以上的版本
 *    3. 在单元测试类上添加RunWith注解
 *       @RunWith(SpringJUnit4ClassRunner.class)
 *    4. 指定有Junit创建核心容器的时候,要加载的配置文件/配置类
 *       @ContextConfiguration(locations = "classpath:applicationContext.xml") 混合开发
 *       @ContextConfiguration(classes = 配置类名.class) 纯注解开发
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class TestSpring {
    @Autowired
    private AccountController accountController;
    @Test
    public void testFindAll() throws SQLException {
        List<Account> accountList = accountController.findAll();
        System.out.println(accountList);
    }
}

 

posted @ 2021-08-06 10:54  春华_秋实  阅读(129)  评论(0编辑  收藏  举报