spring基础2

spring基础2,第1张

Spring注解开发(重点)

本章节我们开始学习Spring的注解,首先明确注解就是对原来XML配置的简化,二者实现的功能是一样的。

Spring常用注解

Spring常用注解主要是替代的配置

注意:使spring的注解生效,必须开启注解组件扫描



    
    
    
    
    

IOC 对象创建
* @Component
    用于实例化对象,相当于配置文件中的
    它支持一个属性value,相当于xml中bean的id,如果不写,默认值为类名的首字母小写
​
* @Controller  @Service  @Repository
    这三个注解的功能跟@Component类似,他们分别标注在不同的层上。
        @Controller  标注在表现层的类上
        @Service     标注在业务层的类上
        @Repository  标注在持久层的类上
    推荐使用这三个,当一个类实在不好归属在这三个层上时,再使用@Component

修改UserDaoImpl

package com.alibaba.dao.impl;
​
import com.alibaba.dao.UserDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
​
//  
// @Component(value = "userDao")
// @Component("userDao")
// @Component // value属性省略,默认值类名首字母小写  value="userDaoMySQL"
@Repository
public class UserDaoMySQL implements UserDao {
​
    @Override
    public void save() {
        System.out.println(" *** 作MySQL数据库");
    }
}
​

修改UserServiceImpl

package com.alibaba.serivce.impl;
​
import com.alibaba.dao.UserDao;
import com.alibaba.serivce.UserService;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
​
//  
// @Component
@Service
public class UserServiceImpl implements UserService {
​
    // 声明dao成员变量
    private UserDao userDao;
​
    // set方法依赖注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
​
    @Override
    public void save() {
        userDao.save();
    }
}
​

DI 注入引用类型
- @Autowired:通过Class类从ioc容器中获得对象实例,相当于 ioc.getBean(Class类型)
​
- @Qualifie:需要跟@Autowired组合使用,相当于 ioc.getBean(id,Class类型)
​
- @Resource:由java提供  @Autowired + @Qualifie

修改UserServiceImpl

//  
// @Component
@Service
public class UserServiceImpl implements UserService {
​
    // 声明dao成员变量
    // 
    // @Autowired
    // @Qualifier("userDaoOracle")
    @Resource(name = "userDaoOracle")
    private UserDao userDao;
​
​
    @Override
    public void save() {
        userDao.save();
    }
}

Bean生命周期
- @Scope:相当于
​
- @PostConstruct:相当于
​
- @PreDestroy:相当于

//  
// @Component
@Service
// @Scope("prototype") // 多例对象 
public class UserServiceImpl implements UserService {
​
    // 声明dao成员变量
    // 
    // @Autowired
    // @Qualifier("userDaoOracle")
    @Resource(name = "userDaoOracle")
    private UserDao userDao;
​
​
    @Override
    public void save() {
        userDao.save();
    }
​
​
    @PostConstruct
    public void init(){
        System.out.println("创建成功了...");
    }
​
    @PreDestroy
    public void destroy(){
        System.out.println("销毁成功了...");
    }
}

DI 注入简单类型
- @Value 相当于:

package com.alibaba.config;
​
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
​
// 
@Component
@Data
public class MyDataSouce {
​
    @Value("${jdbc.driver}")
    private String driverClassName ;  //  
    @Value("${jdbc.url}")
    private String url; //  
    @Value("${jdbc.username}")
    private String username; // 
    @Value("${jdbc.password}")
    private String password; //  
}

知识小结
xml配置注解配置说明
< bean id="" >@Component @Controller @Service @Repositorybean的实例化
< property name="" ref="">@Autowired @Qualifier @Resourcebean的对象属性注入
< property name="" value="">@Valuebean的简单属性注入
< bean scope="">@Scope控制bean的作用范围
< bean init-method="" destroy-method="" />@PostConstruct @PreDestroybean创建之后和销毁之前调用

Spring纯注解

使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置

- @Configuration:相当于applicationContext.xml
​
- @ComponentScan:相当于  
​
- @PropertySource:相当于 
​
- @Bean:将第三方类(对象)交给ioc容器
​
- @Import:相当于       

package com.alibaba.config;
​
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
​
import javax.sql.DataSource;
​
@Configuration // 相当于 applicationContext.xml
@ComponentScan("com.alibaba")//  
@PropertySource("classpath:jdbc.properties")// 
public class SpringConfig {
​
    @Value("${jdbc.driver}")
    private String driverClassName ;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
​
​
​
​
    @Bean(name = "dataSource")// 
    public DataSource createDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driverClassName); // 
        druidDataSource.setUrl(url); // 
        druidDataSource.setUsername(username); //  
        druidDataSource.setPassword(password); //  
​
        return druidDataSource;
    }
}
​

public class UserTest {
​
    @Test
    public void test01()throws Exception{
        // 初始spring环境
        // ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
        ApplicationContext ioc = new AnnotationConfigApplicationContext(SpringConfig.class);
        // 获取指定类型的容器对象
        DataSource dataSource = ioc.getBean(DataSource.class);
        System.out.println(dataSource);
​
    }
}

配置类模块化

知识小结
* spring常用注解
    开启注解组件扫描
         
    ioc相关
        @Component:  工具类、实体类
        @Repository: 持久层
        @Service:    业务层
        @Controller: 表现(示)层
    di相关
        @Autowired 根据类型注入
        @Qualifier 跟@Autowired一起使用,根据id查找
        @Resources 相当于  @Autowired + @Qualifier(了解)
        ------------------------
        @Value  注入简单类型
​
​
* spring新注解
    @ComponentScan
        组件扫描注解。 相当于xml配置文件中的< context:component-scan base-package=""/> 
​
    @Bean
        该注解只能写在方法上,表明使用此方法创建一个对象,并且放入spring容器。它支持一个name属性,用于给生成的bean取一个id。 
​
    @PropertySource
        用于引入其它的properties配置文件
​
    @Import
        在一个配置类中导入其它配置类的内容
​
    @Configuration
        被此注解标注的类,会被Spring认为是配置类。Spring在启动的时候会自动扫描并加载所有配置类,然后将配置类中bean放入容器

Spring整合Junit(会用) 现有测试代码的问题

在普通的测试类中,需开发者手动加载配置文件并创建Spring容器,然后通过Spring相关API获得Bean实例;

如何优化?

我们可以让SpringJunit增强Junit,负责创建Spring容器,开发者就可以直接在测试类注入Bean实例

导入spring-test坐标


    org.springframework
    spring-test
    5.1.5.RELEASE

修改测试类
package com.alibaba.test;
​
import com.alibaba.config.DataSourceConfig;
import com.alibaba.config.SpringConfig;
import com.alibabaa.serivce.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
​
import javax.sql.DataSource;
​
@RunWith(SpringRunner.class) // 修改为spring的运行器
// @ContextConfiguration(locations = "classpath:applicationContext.xml") // new ClassPathXmlApplicationContext();
@ContextConfiguration(classes =SpringConfig.class ) // new AnnotationConfigApplicationContext();
public class UserTest { // 当前这个测试类就在ioc容器内,可以直接完成依赖注入
​
​
    @Autowired
    private DataSource dataSource;
​
    // 测试连接池
    @Test
    public void test01()throws Exception{
        System.out.println(dataSource);
    }
​
    @Autowired
    private UserService userService;
​
    // 测试userService
    @Test
    public void test02()throws Exception{
        userService.save();
    }
​
    @Test
    public void test03()throws Exception{
​
    }
}
​

Spring整合MyBatis(重点) 复习MyBatis 创建java工程,导入坐标

day02-spring02-mybatis


    
    
        mysql
        mysql-connector-java
        5.1.47
    
    
    
        com.alibaba
        druid
        1.1.15
    
    
    
        org.mybatis
        mybatis
        3.5.3
    
    
    
        org.projectlombok
        lombok
        1.18.10
    

    
        
        
            org.apache.maven.plugins
            maven-compiler-plugin
            3.1
            
                1.8
                1.8
                UTF-8
            
        
    

实体类

com.alibaba.domain包下创建

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
​
    private Integer id;
    private String name;
    private Double money;
​
}

Mapper接口

com.alibaba.mapper.AccountMapper.java包下创建

Mapper映射

com/alibaba/mapper/AccountMapper.xml目录下创建

MyBatis主配置文件

测试
package com.alibaba.test;
​
import com.alibaba.domain.Account;
import com.alibaba.mapper.AccountMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
​
import java.io.InputStream;
import java.util.List;
​
public class MyBatisTest {
​
    public static void main(String[] args)throws Exception {
        // 1.加载mybatis主配置文件
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        // 2.构建工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 3.生产会话对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 4.创建代理对象
        AccountMapper accountMapper = sqlSession.getMapper(AccountMapper.class);
        // 5.执行业务逻辑
        List list = accountMapper.findAll();
        for (Account account : list) {
            System.out.println(account);
        }
        // 6.释放资源
        sqlSession.close();
        inputStream.close();
    }
}

整合思想
  • 原本单独使用mybatis框架,需要手动创建:sqlSessionFactory、sqlSession、UserMapper等对象

  • 现在与spring整合之后,将这些对象的创建权交给spring的ioc容器,那么我们的service层就可以进行 @Autowired依赖注入

代码实现 导入整合坐标


    org.springframework
    spring-context
    5.1.5.RELEASE



    junit
    junit
    4.12


    org.springframework
    spring-test
    5.1.5.RELEASE



    org.springframework
    spring-context-support
    5.1.5.RELEASE



    org.springframework
    spring-jdbc
    5.1.5.RELEASE



    org.mybatis
    mybatis-spring
    2.0.3

spring主配置文件
  • 代替:mybatis-config.xml

  • 将mybatis所有的对象创建出来,并交给ioc容器



​
    
    
​
    
    
​
    
    
        
        
        
        
    
​
​
    
    
        
        
        
        
    
​
​
    
    
        
    

测试
  • 我们在spring的测试代码中就可以直接依赖注入mapper代理对象

知识小结

转账案例(大作业)

需求

使用spring框架的常用注解整合MyBatis技术,实现账户转账功能

创建java工程

day02-spring03-transfer


    
    
        mysql
        mysql-connector-java
        5.1.47
    
    
    
        com.alibaba
        druid
        1.1.15
    
    
    
        org.mybatis
        mybatis
        3.5.3
    
    
    
        org.projectlombok
        lombok
        1.18.10
    
​
    
    
        org.springframework
        spring-context
        5.1.5.RELEASE
    
    
    
        junit
        junit
        4.12
    
    
        org.springframework
        spring-test
        5.1.5.RELEASE
    
    
    
        org.springframework
        spring-context-support
        5.1.5.RELEASE
    
    
    
        org.springframework
        spring-jdbc
        5.1.5.RELEASE
    
    
    
        org.mybatis
        mybatis-spring
        2.0.3
    

    
        
        
            org.apache.maven.plugins
            maven-compiler-plugin
            3.1
            
                1.8
                1.8
                UTF-8
            
        
    

编写代码 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
​
    private Integer id;
    private String name;
    private Double money;
​
}

Mapper接口

service接口

service实现类

加入Spring的配置



​
​
    
    
​
​
    
    
    
        
        
        
        
    
​
    
    
        
        
        
        
    
​
​
    
    
        
    

单元测试

日志功能(理解)

需求

在业务层类中的方法中打印日志,记录进入和离开以及发生异常的时间点

创建java工程

day02-spring04-log


    
    
        org.springframework
        spring-context
        5.1.5.RELEASE
    
    
    
        junit
        junit
        4.12
    
    
    
        org.springframework
        spring-test
        5.1.5.RELEASE
    
    
    
        cn.hutool
        hutool-all
        5.2.3
    


    
        
        
            org.apache.maven.plugins
            maven-compiler-plugin
            3.1
            
                1.8
                1.8
                UTF-8
            
        
    

基础代码
package com.alibaba.service.impl;
​
import cn.hutool.core.date.DateUtil;
import com.alibaba.service.AccountService;
import org.springframework.stereotype.Service;
import org.springframework.util.unit.DataUnit;
​
@Service
public class AccountServiceImpl implements AccountService {
​
    @Override
    public void save() {
​
        try {
            System.out.println(DateUtil.now() + "方法执行前...");
            int i = 1 / 0;
​
            System.out.println("保存账户");
            System.out.println(DateUtil.now() + "方法执行成功...");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(DateUtil.now() + "方法出现异常...");
        }
    }
​
    @Override
    public void updateById() {
        try {
            System.out.println(DateUtil.now() + "方法执行前...");
            System.out.println("修改账户");
            System.out.println(DateUtil.now() + "方法执行成功...");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(DateUtil.now() + "方法出现异常...");
        }
    }
}
​

日志工具类
// 日志工具类
@Component
public class Logger {
​
​
    public void before(){
        System.out.println(DateUtil.now() + "方法执行前...");
    }
​
​
    public void after(){
        System.out.println(DateUtil.now() + "方法执行成功...");
    }
​
    public void expcetion(){
        System.out.println(DateUtil.now() + "方法出现异常...");
    }
}

@Service
public class AccountServiceImpl implements AccountService {
​
    @Autowired
    private Logger logger;
​
    @Override
    public void save() {
        try {
            logger.before();
            int i = 1 / 0;
​
            System.out.println("保存账户");
            logger.after();
        } catch (Exception e) {
            e.printStackTrace();
            logger.expcetion();
        }
    }
​
    @Override
    public void updateById() {
        try {
            logger.before();
            System.out.println("修改账户");
            logger.after();
        } catch (Exception e) {
            e.printStackTrace();
            logger.expcetion();
        }
    }
}

现有代码的问题?

通用业务代码:Logger日志

核心业务代码:AccountService账户

硬编码:通用业务代码和核心业务代码写在一起产生了耦合,未来升级改造会相互影响....

JDK动态代理

动态代理: 目标对象(AccountServiceImpl) + 代理逻辑 (Logger) = 代理对象

复制工程
  1. 进入磁盘目录

  2. 进入复制后工程

  3. pom.xml改名(别忘了ctrl+s保存)

  4. idea工程导入

清理service代码

编写一个日志增强工厂
package com.alibaba.factory;
​
import com.alibaba.util.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
​
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
​
// 使用jdk方案实现对目标对象日志增强
@Component
public class JdkProxyFactory {
​
    // 通用代码 日志工具类
    @Autowired
    private Logger logger;
​
    public Object createLogProxy(Object target) { // target:业务代码(目标对象)
        Object jdkProxy = null;
​
        // 创建代理对象
        jdkProxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object obj = null;
                        try {
                            logger.before();
                            // 执行业务代码原有的功能
                            obj = method.invoke(target, args);
                            logger.after();
                        } catch (Exception e) {
                            e.printStackTrace();
                            logger.expcetion();
                        }
​
                        return obj;
                    }
                });
​
        return jdkProxy;
    }
}
​

测试代码
package com.alibaba.test;
​
import com.alibaba.factory.JdkProxyFactory;
import com.alibaba.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
​
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {
​
    @Autowired
    private AccountService accountService; // 目标对象
​
    @Autowired
    private JdkProxyFactory jdkProxyFactory; // 日志代理工厂
​
    @Test
    public void test01() throws Exception {
​
        // 创建代理对象
        AccountService jdkProxy = (AccountService) jdkProxyFactory.createLogProxy(accountService);
        // jdkProxy.save();
        jdkProxy.updateById();
    }
}
​

CGLIB动态代理

动态代理: 目标对象(AccountServiceImpl) + 代理逻辑 (Logger) = 代理对象

复制工程

参考jdk方式

使用cglib

虽然cglib动态代理是第三方技术,但本次工程不需要导坐标

因为spring框架已经内置了...

编写日志增强工厂
// 基于cglib技术生产代理对象工厂
@Component
public class CglibProxyFactory {
​
    @Autowired
    private Logger logger;
​
    // 创建日志代理对象
    public Object createCglibProxy(Object target) {
        Object cglibProxy = null;
​
        /*
            参数一:目标对象的class
            参数二:增强逻辑
        * */
        cglibProxy = Enhancer.create(target.getClass(), new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                Object obj = null;
                try {
                    logger.before();
                    // 调用业务代码核心功能
                    obj = method.invoke(target, objects);
                    logger.after();
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.expcetion();
                }
​
                return obj;
            }
        });
​
        return cglibProxy;
    }
}

修改测试代码
package com.alibaba.test;
​
import com.alibaba.factory.CglibProxyFactory;
import com.alibaba.service.impl.AccountServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
​
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {
​
    @Autowired
    private AccountServiceImpl accountService; // 目标对象
​
    @Autowired
    private CglibProxyFactory cglibProxyFactory; // 代理工厂
​
    @Test
    public void test01() throws Exception {
​
        // accountService.updateById();
​
        // 生产代理对象
        AccountServiceImpl cglibProxy = (AccountServiceImpl) cglibProxyFactory.createCglibProxy(accountService);
​
        // cglibProxy.updateById();
        cglibProxy.save();
    }
}

总结 jdk和cglib两种代理方式的选择?

jdk动态技术性能优于cglib的

  1. 如果目标对象有接口,一定选择jdk方案

  2. 如果目标对象没有接口,次选cglib方案

当核心业务(保存)和增强业务(日志)同时出现时?

开发期:分开编写 accountService(核心业务)、logger(通用业务)

运行期:将日志代码 动态切入到核心代码

这么做的好处:在不破坏核心业务代码的情况下,实现对目标对象增强

设计思想:AOP:面向切面编程

实现技术:动态代理(jdk、cglib)

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/877872.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-13
下一篇2022-05-13

发表评论

登录后才能评论

评论列表(0条)

    保存