Spring中

Spring

IOC容器XML配置【案例】

JdbcTemplate 操作数据库

介绍

JdbcTemplate是Spring提供的一个模板类,它是对jdbc的封装。用于支持持久层的操作。它的特点是:简单、方便。它简化了JDBC的使用并有助于避免常见错误。它执行核心的JDBC工作流程,留下应用程序代码以提供SQL并提取结果。query update pstmt.executeUpdate(insert,update,delete ,create,drop)

此类执行SQL查询或更新,在ResultSets上启动迭代并捕获JDBC异常,并将其转换为org.springframework.dao 程序包中定义的通用异常,信息量更大的异常层次结构 。

环境准备

步骤:

1、创建表、录入数据

2、创建项目spring02_00_jdbcTemplate,添加依赖

3、编写实体类

实现:

1、创建表、录入数据
在这里插入图片描述
2、创建项目spring02_00_jdbcTemplate,添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>xxx.xxx</groupId>
    <artifactId>spring02_00_jdbcTemplate</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--spring核心支持包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
        <!--springjdbc支持包,提供了JdbcTemplate-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
        <!--数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>        
    </dependencies>
    
</project>

2、编写实体类

public class Account {
    private Integer accountId;
    private Integer uid;
    private Double money;
    // 省略get、set、toString()
}

测试类

查询

需求

  • 实现查询全部
  • 实现根据ID查询

实现


public class App1_query {

    private JdbcTemplate jdbcTemplate;

    // 初始化:每次再执行junit方法之前先执行@Before注解修饰的方法
    @Before
    public void before(){
        // 创建连接池
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///mybatis");
        dataSource.setUsername("root");
        dataSource.setPassword("root");

        // 创建jdbc模板工具类
        jdbcTemplate = new JdbcTemplate(dataSource);
    }


    /**
     * 1. 查询全部 (查询列与对象属性一致)
     * 注意:
     *   A. 通过BeanPropertyRowMapper把查询结果自动封装为对象;
     *   B. 要求:查询的列,一定要与对象属性一致,才可以正确封装(不区分大小写)
     */
    @Test
    public void findAll_1() {
        // 查询
        List<Account> list = jdbcTemplate.query(
                "select * from account",
                new BeanPropertyRowMapper<Account>(Account.class));
        System.out.println("list = " + list);
    }

    /**
     * 2. 查询全部 (查询列与对象属性不一致)
     */
    @Test
    public void findAll_2() {
        String sql = "SELECT accountId id,uid userId,money FROM account";
        List<Account> list = jdbcTemplate.query(sql, new RowMapper<Account>() {

            // 参数1:结果集;参数2:当前的行号
            @Override
            public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
                // 告诉jdbcTemplate,如何封装一行数据
                Account account = new Account();
                account.setAccountId(rs.getInt("id"));
                account.setUid(rs.getInt("userId"));
                account.setMoney(rs.getDouble("money"));
                return account;
            }
        });

        System.out.println("list = " + list);
    }

    /**
     * 根据主键查询
     */
    @Test
    public void findById() {
        // 查询条件
        Integer id = 1;
        // 执行查询
        Account account =
                jdbcTemplate.queryForObject(
                        "select * from account where accountId=?",
                        new BeanPropertyRowMapper<Account>(Account.class),id);
        System.out.println(account);
    }
}

更新

public class App2_update {

    private JdbcTemplate jdbcTemplate;

    // 初始化:每次再执行junit方法之前先执行@Before注解修饰的方法
    @Before
    public void before(){
        // 创建连接池
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///mybatis");
        dataSource.setUsername("root");
        dataSource.setPassword("root");

        // 创建jdbc模板工具类
        jdbcTemplate = new JdbcTemplate(dataSource);
    }


    @Test
    public void delete() {
        //jdbcTemplate.update("insert/update/delete/drop/create...");

        // 执行删除  (增删改都是update方法)
        jdbcTemplate.update("delete from account where accountId=?",3);
    }
}

使用 spring 的 IOC 的实现账户的CRUD(一)搭建环境

需求

  1. 实现账户表的crud操作:

  2. 表结构
    在这里插入图片描述

  3. 技术架构

    A. 对象管理: IOC容器

    B. 操作数据库: JdbcTemplate

    C. 连接池: 使用DruidDataSource

步骤

搭建好项目的环境:

  1. 创建项目:spring02_01_crud_xml
  2. 添加依赖
  3. 编写Account实体类
  4. 编写dao接口、实现
  5. 编写service接口、实现

实现

搭建好项目的环境:

  1. 创建项目:spring02_01_crud_xml

  2. 添加依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>xxx.xxx</groupId>
        <artifactId>spring02_01_crud_xml</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.1.8.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.1.8.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.10</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
        </dependencies>
        
    </project>
    
  3. 编写Account实体类

    public class Account {
        private Integer accountId;
        private Integer uid;
        private Double money;
    
        public Integer getAccountId() {
            return accountId;
        }
    
        public void setAccountId(Integer accountId) {
            this.accountId = accountId;
        }
    
        public Integer getUid() {
            return uid;
        }
    
        public void setUid(Integer uid) {
            this.uid = uid;
        }
    
        public Double getMoney() {
            return money;
        }
    
        public void setMoney(Double money) {
            this.money = money;
        }
    
        @Override
        public String toString() {
            return "Account{" +
                    "accountId=" + accountId +
                    ", uid=" + uid +
                    ", money=" + money +
                    '}';
        }
    }
    
    
  4. 编写dao接口、实现

    接口

    public interface AccountDao {
        /**
         * 添加
         * @param account
         */
        void save(Account account);
    
        /**
         * 修改
         * @param account
         */
        void update(Account account);
    
        /**
         * 删除
         * @param id
         */
        void delete(Integer id);
    
        /**
         * 查询全部
         * @return
         */
        List<Account> findAll();
    
        /**
         * 根据id查询
         * @param id 查询的主键
         * @return 根据主键查询的记录封装的对象并返回
         */
        Account findById(Integer id);
    }
    
    

    实现

    
    public class AccountDaoImpl implements AccountDao {
    
        private JdbcTemplate jdbcTemplate;
        // 交给springioc容器注入jdbcTemplate对象。 (依赖注入)
        public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        }
    
        @Override
        public void save(Account account) {
            jdbcTemplate.update("insert into account values(null,?,?)",account.getUid(),account.getMoney());
        }
    
        @Override
        public void update(Account account) {
            jdbcTemplate.update("update account set uid=?,money=? where accountId=?",
                    account.getUid(),account.getMoney(),account.getAccountId());
        }
    
        @Override
        public void delete(Integer id) {
            jdbcTemplate.update("delete from account where accountId=?",id);
        }
    
        @Override
        public List<Account> findAll() {
            return jdbcTemplate.query("select * from account",new BeanPropertyRowMapper<Account>(Account.class));
        }
    
        @Override
        public Account findById(Integer id) {
            return jdbcTemplate.queryForObject("select * from account where accountId=?",
                    new BeanPropertyRowMapper<Account>(Account.class),id);
        }
    }
    
    
  5. 编写service接口、实现

    public interface AccountService {
        /**
         * 添加
         * @param account
         */
        void save(Account account);
    
        /**
         * 修改
         * @param account
         */
        void update(Account account);
    
        /**
         * 删除
         * @param id
         */
        void delete(Integer id);
    
        /**
         * 查询全部
         * @return
         */
        List<Account> findAll();
    
        /**
         * 根据id查询
         * @param id 查询的主键
         * @return 根据主键查询的记录封装的对象并返回
         */
        Account findById(Integer id);
    }
    
    
    public class AccountServiceImpl implements AccountService {
    
        private AccountDao accountDao;
        // 交给ioc容器,依赖注入dao对象
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
        @Override
        public void save(Account account) {
            accountDao.save(account);
        }
    
        @Override
        public void update(Account account) {
            accountDao.update(account);
        }
    
        @Override
        public void delete(Integer id) {
            accountDao.delete(id);
        }
    
        @Override
        public List<Account> findAll() {
            return accountDao.findAll();
        }
    
        @Override
        public Account findById(Integer id) {
            return accountDao.findById(id);
        }
    }
    
    

使用 spring 的 IoC 的实现账户的CRUD(二)spring配置

步骤

1、编写applicationContext.xml ,SpringIOC容器配置

2、测试

实现

1、编写applicationContext.xml ,SpringIOC容器配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--创建连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql:///mybatis"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <!--创建JdbcTemplate,注入连接池-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--创建dao,注入JdbcTemplate-->
    <bean id="accountDao" class="xxx.xxx.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

    <!--创建service,注入dao-->
    <bean id="accountService" class="xxx.xxx.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>
</beans>

2、测试


public class App {
    @Test
    public void find() {
        // 创建容器
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        // 从容器中获取指定名称的service
        AccountService accountService = (AccountService) ac.getBean("accountService");
        // 调用service方法
        System.out.println(accountService.findAll());
    }

    @Test
    public void save() {
        // 创建容器
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        // 从容器中获取指定名称的service
        AccountService accountService = (AccountService) ac.getBean("accountService");
        // 调用service方法
        Account account = new Account();
        account.setUid(41);  // 注意:uid是外键
        account.setMoney(9999D);

        accountService.save(account);
    }
}

补充

可以把数据库连接信息放到jdbc.properties中,方便统一管理。所以项目可以先引入jdbc.properties文件

如何在springioc容器中,加载jdbc.properties配置文件?
在这里插入图片描述
核心代码

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

    <!--加载jdbc.properties配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--创建连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    ...
    ...
    ...

注解的使用

基于注解的 IOC 配置(一)环境搭建

实现

步骤:

  1. 创建项目:spring02_02_annotation
  2. 添加依赖
  3. 创建User1对象
  4. 编写bean.xml配置文件
  5. 测试

实现:

  1. 创建项目:spring02_02_annotation

  2. 添加依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>xxx.xxx</groupId>
        <artifactId>spring02_02_annotation</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.1.8.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
        </dependencies>
        
    </project>
    
  3. 创建User1对象

    
    /**
     * @Component
     * 1. 作用:创建对象,加入ioc容器
     * 2. 需要开启注解扫描
     * 3. 加入容器的对象名称,默认是类名,首字母小写
     *    相当于:<bean id="user1" class="..."/>
     * 4. 也可以指定加入容器中的对象的名称
     *    @Component          默认加入容器的对象名称是类名首字母小写,如:user1
     *    @Component("user")  加入容器的对象名称是user
     */
    @Component("user")
    public class User1 {
        public User1(){
            System.out.println("创建User对象");
        }
    }
    
    
  4. 编写bean.xml配置文件

    <?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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
    
        <!--
            component-scan 开启注解扫描
              base-package
                1. 指定扫描的包,会扫描当前包下所有的类,以及当前包下的所有子包下所有的类 (基包)
                2. 如果扫描多个包逗号隔开
        -->
        <context:component-scan base-package="xxx.xxx.entity"/>
    </beans>
    
  5. 测试

    
    public class App {
        @Test
        public void test() {
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
            User1 user = (User1) ac.getBean("user");
            System.out.println(user);
        }
    }
    
    

基于注解的 IOC 配置(二)用于创建对象的注解

注解说明

@Controller: 一般用于表现层的注解。 同@Component
@Service: 一般用于业务层的注解。 同@Component
@Repository: 一般用于持久层的注解。 同@Component

@Component: 除了上述情形以外的工具类,使用此注解。

代码演示

//@Controller("user") 用于修饰表现层的类  web
//@Service("user")    用于修饰业务层的类  service
//@Repository("user") 用于修饰持久层的类  dao
@Component("user")    // 除了上面以外的类都用@component注解,比如工具类
public class User2 {
    public User2(){
        System.out.println("创建User对象");
    }
}

基于注解的 IOC 配置(三)注入数据 A @Autowired修饰字段

注解说明

  1. @Autowired 作用: 给对象中的属性赋值
  2. 修饰在字段上,会自动根据字段的类型、或名称进行注入。

代码

第一步:

@Component("user")
public class User3 {

    /**
     * @Autowired
     * 1. 作用:依赖注入的注解,给属性赋值。
     * 2. 根据类型,自动去容器中找@Autowired修饰的字段的类型注入(赋值)。
     * 3. 根据名称,如果容器中该类型对应的对象有多个,会根据字段名称去容器中找对象注入。
     *            如果根据名称没有找到,就报错。
     * 4. 属性
     *    required
     *       true 默认,表示去容器中找对象注入,没有找到就报错。
     *       false 去容器中找对象,找不到不报错,默认NULL值
     */
    @Autowired
    private String name;

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

第二步:

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


    <!--
        component-scan 开启注解扫描
          base-package
            1. 指定扫描的包,会扫描当前包下所有的类,以及当前包下的所有子包下所有的类 (基包)
            2. 如果扫描多个包逗号隔开
    -->
    <context:component-scan base-package="xxx.xxx.entity"/>

    <!--创建一个字符串-->
    <bean id="str" class="java.lang.String">
        <constructor-arg value="Rose"/>
    </bean>

    <bean id="name" class="java.lang.String">
        <constructor-arg value="Jacky"/>
    </bean>
</beans>

基于注解的 IOC 配置(四)注入数据 B @Autowired修饰方法

代码

@Autowired修饰方法与修饰在字段上基本一样:

第一步:

@Component("user")
public class User4 {

    /**
     * 1. 根据形参类型,去容器中找该类型的对象注入。
     * 2. 如果该类型的对象有多个,就根据形参名称注入
     */
    @Autowired
    public void setName(String name){
        System.out.println(name);
    }
}

第二步:

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

    <context:component-scan base-package="xxx.xxx.entity"/>

    <!--创建一个字符串-->
    <bean id="str" class="java.lang.String">
        <constructor-arg value="Rose"/>
    </bean>

    <bean id="name" class="java.lang.String">
        <constructor-arg value="Jacky"/>
    </bean>
</beans>

基于注解的 IOC 配置(四)注入数据 C @Qualifier

注解说明

  1. @Autowired注解,只能根据字段类型或字段名称注入,不能自己指定名称注入。
  2. @Qualifier注解可以让@Autowired注解按照指定名称注入。
  3. 通常:@Autowired与@Qualifier都是配置一起使用的。

代码

@Component("user")
public class User5 {

    /**
     * @Qualifier
     * 1. 可以让@Autowired根据指定的名称注入
     * 2. 通常:@Autowired与@Qualifier要配置使用。
     * 3. @Qualifier可以单独使用,用于方法参数。
     */
    @Autowired
    @Qualifier("str2") // 去容器中找str2对应的对象注入
    private String name;

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

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

    <context:component-scan base-package="xxx.xxx.entity"/>

    <!--创建一个字符串-->
    <bean id="str1" class="java.lang.String">
        <constructor-arg value="Rose"/>
    </bean>

    <bean id="str2" class="java.lang.String">
        <constructor-arg value="Jacky"/>
    </bean>
</beans>

基于注解的 IOC 配置(五)注入数据 D @Resource

注解说明

  1. 在jdk1.8以后版本不提供支持
  2. @Resource可以根据名称、类型注入

代码

@Component("user")
public class User6 {

    /**
     * @Resource
     * 1. 实现依赖注入的注解
     * 2. 相当于@Autowired + @Qualifier
     * 3. 通过属性指定只根据名称或类型注入
     */
    @Resource(name = "str2")
    private String name;

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

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

    <context:component-scan base-package="xxx.xxx.entity"/>

    <!--创建一个字符串-->
    <bean id="str1" class="java.lang.String">
        <constructor-arg value="Rose"/>
    </bean>

    <bean id="str2" class="java.lang.String">
        <constructor-arg value="Jacky"/>
    </bean>
</beans>

基于注解的 IOC 配置(六)注入数据 E @Value

注解说明

  1. 直接给简单类型的属性赋值
  2. 也可以配置文件取值

代码

@Component("user")
public class User7 {

    /**
     * @Value
     * 1. 直接给简单类型的字段赋值
     * 2. 也可以获取配置文件值
     */
    @Value("100")
    private int id;
    @Value("Jacky")
    private String name;

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

基于注解的 IOC 配置(七)对象范围与生命周期相关注解

注解说明

@Scope 【对象范围】

@PostConstruct 【修饰初始化方法,创建对象之后执行】

@PreDestroy 【修饰回收资源方法,销毁容器之前执行】

@Lazy 【延迟初始化】

对比XML:

<bean id=""  class="" 
      scope=""                使用@Scope注解取代
      init-method=""          使用@PostConstruct注解取代
      destroy-method=""       使用@PreDestroy注解取代
      lazy-init=""            使用@Lazy注解取代
/>

代码

第一步:

/**
 * @Scope
 * 1. 对象范围
 * 2. 属性值
 *    singleton 默认单例
 *    prototype 表示多例
 * @Lazy
 * 1. 对象懒加载
 * 2. 如果不设置默认单例的对象在创建容器时候就创建,设置懒加载后,第一次用的时候才创建
 * @PostConstruct
 * 1. 执行初始化操作
 * 2. 创建对象之后执行
 * @PreDestroy
 * 1. 用于释放资源
 * 2. 对单例有效,且要调用容器的close()方法才触发
 */
@Component("user")
@Scope("singleton")
@Lazy
public class User8 {

    public User8(){
        System.out.println("创建User对象");
    }

    @PostConstruct
    public void init(){
        System.out.println("..init");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("...destroy");
    }

}

第二步:测试

public class App {
    @Test
    public void test() {
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        User8 temp1 = (User8) ac.getBean("user");
        User8 temp2 = (User8) ac.getBean("user");

        System.out.println(temp1);
        System.out.println(temp2);

        // 销毁容器
        ac.close();
    }
}

总结

12个注解

  • 创建对象相关的四个注解:

@Component 创建对象加入容器, 例如:工具类、其他组件

@Repository 创建对象加入容器, 例如:标识数据库访问层的组件

@Service 创建对象加入容器, 例如:标识乐屋逻辑层的组件

@Controller 创建对象加入容器 例如:标识控制层的组件

  • 依赖注入的四个注解:

@Autowired 从容器中找对象,给属性赋值。根据类型、名称去容器中找。

@Qualifier 结合Autowired使用,可以指定按照名称去容器找对象注入。

@Value 给简单类型属性直接赋值/获取配置文件值@Value(“${key}”)

@Resource 从容器中找对象,给属性赋值。 根据名称、类型去容器中查找

  • 生命周期相关的四个注解:

@Scope 单例/多例

@PostConstruct 初始化方法,创建对象后执行

@PreDestroy 销毁先执行

@Lazy 延迟初始化

  • 重点:@Component 、@Autowired

使用原则

使用原则:

  1. 无论使用XML还是注解实现IOC容器,底层实现都是一样的,做的时期都是一样的。
  2. 这两种都要掌握:XML、注解
  3. 哪种方便我们开发使用,就应用哪些方式(注解、xml)

项目应用

推荐用法:

  1. 自己写的类用注解
  2. jar包中的类用XML

注解改造【案例】

案例:使用注解改造案例

实现

步骤:

  1. 创建项目、准备项目环境:spring02_03_crud_anno
  2. 修改dao实现
  3. 修改service实现
  4. 修改applicationContext.xml配置

实现:

  1. 创建项目、准备项目环境:spring02_03_crud_anno

  2. 修改dao实现

    // 创建对象,加入容器,加入容器的对象名称默认是类名首字母小写:accountDaoImpl
    @Repository
    public class AccountDaoImpl implements AccountDao {
    
        // 依赖注入:去容器中找JdbcTemplate类型的对象。
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        .....
    }
    
    
  3. 修改service实现

    // 创建对象,加入容器,加入容器的对象名称默认是类名首字母小写:accountServiceImpl
    @Service
    public class AccountServiceImpl implements AccountService {
    
        // 依赖注入:去容器中找AccountDao类型的对象注入
        @Autowired
        private AccountDao accountDao;
    	....
    }
    
    
  4. 修改applicationContext.xml配置

    <?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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--开启注解扫描-->
        <context:component-scan base-package="xxx.xxx"/>
    
        <!--加载jdbc.properties配置文件-->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <!--创建连接池-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <!--创建JdbcTemplate,注入连接池-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    </beans>
    

测试


public class App {
    @Test
    public void find() {
        // 创建容器
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        // 从容器中获取指定名称的service
        AccountService accountService = (AccountService) ac.getBean("accountServiceImpl");
        // 调用service方法
        System.out.println(accountService.findAll());
    }

    @Test
    public void save() {

        ApplicationContext ac =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        // 根据类型去容器中找该类型对象的对象
        AccountService accountService = ac.getBean(AccountService.class);
        // 调用service方法
        Account account = new Account();
        account.setUid(41);  // 注意:uid是外键
        account.setMoney(9999D);

        accountService.save(account);
    }
}

纯注解改造【案例】

spring纯注解配置(一)环境准备

关键点

  1. 想要取代配置,使用纯注解。就要找到每一个配置对应的注解
  2. 分析: 涉及哪些注解?
    在这里插入图片描述

spring纯注解配置(二)@Configuration与@ComponentScan

注解说明

@Configuration 用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。 获取容器时需要使用AnnotationApplicationContext(有@Configuration 注解的类.class)。

@ComponentScan用于指定 spring 在初始化容器时要扫描的包。
作用和在 spring 的 xml 配置文件中的:<context:component-scan base-package=“xxx.xxx”/>是一样的。

代码

步骤

  1. 准备项目环境、创建项目:spring02_04_crud_fullanno
  2. 编写SpringConfiguration, 注解配置类
  3. 修改测试

实现:…

  1. 准备项目环境、创建项目:spring02_04_crud_fullanno

  2. 编写SpringConfiguration, 注解配置类
    在这里插入图片描述

    
    /**
     * @Configuration
     * 1. 修饰一个配置管理类,用来取代配置文件
     * 2. 创建容器时候,加载此注解修饰的类即可
     *
     * @ComponentScan
     * 1. 注解扫描
     * 2. 取代<context:component-scan base-package="xxx.xxx"/>这里的配置
     *
     */
    @Configuration
    @ComponentScan("xxx.xxx")
    public class SpringConfiguration {
    }
    
    
  3. 修改测试 (现在还不能执行,没有写完…)

    public class App {
        @Test
        public void find() {
            // 创建容器
            ApplicationContext ac =
                    new AnnotationConfigApplicationContext(SpringConfiguration.class);
            // 从容器中获取对象
            AccountService accountService = ac.getBean(AccountService.class);
            // 查询
            System.out.println(accountService.findAll());
        }
    
    }
    
    

spring纯注解配置(三)@Bean、@PropertySource、@Import

注解说明

@Bean 该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器

@PropertySource 用于加载.properties 配置文件

@Import用于导入其他配置类

代码

  1. 定义一个类:JdbcConfig, 作用:封装数据库操作相关的配置(如:加载配置文件、创建连接池、创建JdbcTemplate)

    
    /**
     * @PropertySource
     * 1. 加载配置文件
     * 2. 相当于<context:property-placeholder location=""/>
     */
    @PropertySource("jdbc.properties")
    public class JdbcConfig {
    
        /*配置文件取值*/
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
        /**
         * 需求:定义方法,返回连接池,且加入容器
         * @Bean
         *    1. 修改在方法上,自动把方法返回的对象加入ioc容器;
         *    2. name属性,可以指定加入容器的对象的名称(可选)
         *       举例:@Bean(name = "dataSource")
         *
         */
        @Bean
        public DataSource createDataSource(){
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName(this.driver);
            dataSource.setUrl(this.url);
            dataSource.setUsername(this.username);
            dataSource.setPassword(this.password);
            return dataSource;
        }
    
        /**
         * 创建jdbc模板工具类对象,加入容器。
         * @Bean
         *   1. 自动把方法返回的对象加入容器
         *   2. 如果方法有参数,自动根据参数的类型进行注入;如果类型有多个,根据形参名称注入
         */
        @Bean
        public JdbcTemplate createJdbcTemplate(DataSource dataSource){
            return new JdbcTemplate(dataSource);
        }
    }
    
  2. 在SpringConfiguration类中加载JdbcConfig

    
    /**
     * @Configuration
     * 1. 修饰一个配置管理类,用来取代配置文件
     * 2. 创建容器时候,加载此注解修饰的类即可
     *
     * @ComponentScan
     * 1. 注解扫描
     * 2. 取代<context:component-scan base-package="xxx.xxx"/>这里的配置
     *
     * @Import
     * 1. 加载其他的配置管理类
     * 2. 相当于配置文件中的<import resource=""/>
     */
    @Configuration
    @ComponentScan("xxx.xxx")
    @Import(JdbcConfig.class)
    public class SpringConfiguration {
    }
    
    

spring纯注解配置(四)测试@Qualifier单独使用

修改JdbcConfig.java


/**
 * @PropertySource
 * 1. 加载配置文件
 * 2. 相当于<context:property-placeholder location=""/>
 */
@PropertySource("jdbc.properties")
public class JdbcConfig {

    /*配置文件取值*/
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    /**
     * 需求:定义方法,返回连接池,且加入容器
     * @Bean
     *    1. 修改在方法上,自动把方法返回的对象加入ioc容器;
     *    2. name属性,可以指定加入容器的对象的名称(可选)
     *       举例:@Bean(name = "dataSource")
     *
     */
    @Bean(name = "dataSource1")
    public DataSource createDataSource1(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(this.driver);
        dataSource.setUrl(this.url);
        dataSource.setUsername(this.username);
        dataSource.setPassword(this.password);
        return dataSource;
    }

    @Bean(name = "dataSource2")
    public DataSource createDataSource2(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(this.driver);
        dataSource.setUrl(this.url);
        dataSource.setUsername(this.username);
        dataSource.setPassword(this.password);
        return dataSource;
    }

    /**
     * 创建jdbc模板工具类对象,加入容器。
     * @Bean
     *   1. 自动把方法返回的对象加入容器
     *   2. 如果方法有参数,自动根据参数的类型进行注入;如果类型有多个,根据形参名称注入
     *
     * @Qualifier
     *   1. 一般配置@Autowired一起使用
     *   2. 也可以单独使用,修饰在方法的参数上,根据指定的名称注入。
     */
    @Bean
    public JdbcTemplate createJdbcTemplate(@Qualifier("dataSource1") DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }
}

测试
在这里插入图片描述

Spring整合Junit

应用

  • 为什么要Spring整合Junit? 主要是为了简化测试而已。

  • 如果要用spring整合junit,需要先引入spring-test包

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
  • 纯注解开发,Spring整合Junit测试:

// 指定容器加载类
@RunWith(SpringJUnit4ClassRunner.class)
// 指定注解配置类
@ContextConfiguration(classes = SpringConfiguration.class)
public class App2 {

    // 依赖注入,容器中的对象
    @Autowired
    private AccountService accountService;

    @Test
    public void find() {
        System.out.println(accountService.findAll());
    }

}
  • XML配置,Spring整合Junit测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class App2 {
    @Autowired
    private AccountService accountService;

    @Test
    public void find() {
        System.out.println(accountService.findAll());
    }
}

动态代理回顾

代理---->Aop----->Spring声明式事务

Aop原理之代理(一)代理的介绍

概念

  • 什么是代理?

    Proxy,代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。

  • java中动态代理分为哪两类?

    • jdk动态代理,接口代理
    • cglib动态代理,子类代理
  1. 代理的好处

    不用修改目标对象, 实现在目标对象的基础上进行扩展。

Aop原理之代理(二)案例需求、环境搭建

案例需求

已经实现了账户的crud,要求在执行账户的crud的时候,自动记录日志。

模拟实现需求

在service方法中增加打印日志输出的代码,默认日志的记录。

    @Override
    public List<Account> findAll() {
        System.out.println("执行AccountServiceImpl类的save()方法开始:");
        List<Account> list = accountDao.findAll();
        System.out.println("执行AccountServiceImpl类的save()方法结束:");
        return list;
    }

测试
在这里插入图片描述

Aop原理之代理(三)JDK动态代理

介绍

通过JDK动态代理实现对service方法增强,添加自动记录日志的实现。
在这里插入图片描述

代码

现在在junit测试中创建目标对象的代理对象,通过代理实现对目标方法添加日志记录的实现。


public class App3_proxy {
    @Test
    public void find() {
        // 创建容器
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        // 从容器中获取对象  【目标对象】
        AccountService accountService = (AccountService) ac.getBean("accountServiceImpl");

        /**
         * 对目标对象accountService,生成代理
         * JDK动态代理
         * 1、API
         * |-- Proxy
         *   |-- static Objeject newProxyInstance(
         *      参数1:类加载器,
         *      参数2:目标对象实现的接口类型数组,
         *      参数3:事件处理器,当执行代理方法时候会触发事件处理程序,传入当前执行的方法
         *   )
         * 2、原理
         *   在运行时期动态生成字节码,通过动态生成的字节码创建对象。
         *   原理:class $Proxy123 implements AccountService
         *   使用:AccountService proxy = $Proxy123;
         */
        AccountService proxy = (AccountService) Proxy.newProxyInstance(
                this.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 当前执行的方法名称
                        String methodName = method.getName();
                        // 当前执行的类全名
                        String className = method.getDeclaringClass().getName();

                        try {
                            System.out.println("执行"+className+"类"+methodName+"()方法,开始:");
                            // 调用目标对象的方法
                            Object obj = method.invoke(accountService,args);
                            System.out.println("执行"+className+"类"+methodName+"()方法,结束。");
                            return obj;
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                            System.out.println("执行"+className+"类"+methodName+"()方法,出现异常。");
                            return null;
                        }
                    }
                });

        // 执行代理的方法
        List<Account> list = proxy.findAll();
        System.out.println("list = " + list);
    }

}

测试

在这里插入图片描述
注意:现在service方法中并没有记录日志,而执行结果有日志输出!

    @Override
    public List<Account> findAll() {
        List<Account> list = accountDao.findAll();
        return list;
    }

Aop原理之代理(四)CGLIB动态代理

分析

  1. 目标对象: class AccountServiceImpl

  2. jdk代理: class Proxy0 implements AccountService

  3. cglib子类代理: class $Cglib12345 extends AccountServiceImpl

    cglib子类代理: 在运行时期动态生成目标对象的子类,从而对目标对象进行扩展。

  4. 目前spring-core包中已经包含cglib的功能,所以可以直接使用。老旧spring版本需要额外导入cglib包。
    在这里插入图片描述

实现

第一步:对目标对象,通过cglib生成代理


public class App4_proxy_cglib {
    @Test
    public void find() {
        // 创建容器
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        // 从容器中获取对象  【目标对象】
        AccountServiceImpl accountServiceImpl =
                (AccountServiceImpl) ac.getBean("accountServiceImpl");

        /**
         * cglib动态代理(子类代理)
         */
        AccountServiceImpl proxy = (AccountServiceImpl) Enhancer.create(accountServiceImpl.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                // 当前执行的方法名称
                String methodName = method.getName();
                // 当前执行的类全名
                String className = method.getDeclaringClass().getName();

                try {
                    System.out.println("执行"+className+"类"+methodName+"()方法,开始:");
                    // 调用目标对象的方法
                    Object obj = method.invoke(accountServiceImpl,args);
                    System.out.println("执行"+className+"类"+methodName+"()方法,结束。");
                    return obj;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    System.out.println("执行"+className+"类"+methodName+"()方法,出现异常。");
                    return null;
                }
            };
        });


        System.out.println(proxy.getClass());
        // 执行代理的方法
        List<Account> list = proxy.findAll();
        System.out.println("list = " + list);
    }

}

第二步:测试结果
在这里插入图片描述

AOP与Java中代理关系

AOP与Java中代理有什么关系?

  1. Aop编程中:

    1. 如果目标对象有实现接口,spring的aop会使用jdk动态代理;
    2. 如果目标对象没有实现接口,spring的aop会使用cglib子类代理(目标对象不能为final)。
  2. 使用了Spring的Aop编程,代理就不用自己写了。(已经提供了代理的工厂)

注意:

​ 如果使用jdk动态代理,必须通过接口的引用接收代理对象,不能通过实现类接收。

​ 比如下面的代码会报错:

 @Test
    public void find() {
        // 创建容器
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        // 从容器中获取对象  【目标对象】
        AccountService accountService = (AccountService) ac.getBean("accountServiceImpl");
       // System.out.println(accountService.findAll());


        // 对目标对象生成代理
        // 原理: class $Proxy123 implements AccountService
        // AccountService a1 = $Proxy123;        OK
        // AccountServiceImpl a2 = $Proxy123;    NOK  类型转换错误 ClassCastException
       
        //AccountService proxy = (AccountService) Proxy.newProxyInstance( 正确的代码
        AccountServiceImpl proxy = (AccountServiceImpl) Proxy.newProxyInstance(
                this.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("在不修改原有方法的基础上,扩展输出日志功能");
                        return method.invoke(accountService,args);
                    }
                });
        // 生成的代理类  class com.sun.proxy.$Proxy13
        System.out.println(proxy.getClass());

        // 调用代理方法
        System.out.println(proxy.findAll());
    }

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值