Spring

Spring

一、概述:

以下概述内容拷自w3cschool

Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。
Spring 框架是一个开源的 Java 平台,它最初是由 Rod Johnson 编写的,并且于 2003 年 6 月首次在 Apache 2.0 许可下发布。
Spring 是轻量级的框架,其基础版本只有 2 MB 左右的大小。
Spring 框架的核心特性是可以用于开发任何 Java 应用程序,但是在 Java EE 平台上构建 web 应用程序是需要扩展的。
Spring 框架的目标是使 J2EE 开发变得更容易使用,通过启用基于 POJO 编程模型来促进良好的编程实践。

两大重点:

1.依赖注入(DI)
Spring 最认同的技术是控制反转的依赖注入(DI)模式。控制反转(IoC)是一个通用的概念,它可以用许多不同的方式去表达,依赖注入仅仅是控制反转的一个具体的例子。

当编写一个复杂的 Java 应用程序时,应用程序类应该尽可能的独立于其他的 Java 类来增加这些类可重用可能性,当进行单元测试时,可以使它们独立于其他类进行测试。依赖注入(或者有时被称为配线)有助于将这些类粘合在一起,并且在同一时间让它们保持独立。

到底什么是依赖注入?让我们将这两个词分开来看一看。这里将依赖关系部分转化为两个类之间的关联。例如,类 A 依赖于类 B。现在,让我们看一看第二部分,注入。所有这一切都意味着类 B 将通过 IoC 被注入到类 A 中。

依赖注入可以以向构造函数传递参数的方式发生,或者通过使用 setter 方法 post-construction。依赖注入是 Spring 框架的核心部分。

2.面向切面的程序设计(AOP):
Spring 框架的一个关键组件是面向切面的程序设计(AOP)框架。一个程序中跨越多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样常见的很好的关于方面的例子,比如日志记录、声明性事务、安全性,和缓存等等。

在 OOP 中模块化的关键单元是类,而在 AOP 中模块化的关键单元是切面。AOP 帮助你将横切关注点从它们所影响的对象中分离出来,然而依赖注入帮助你将你的应用程序对象从彼此中分离出来。

Spring 框架的 AOP 模块提供了面向切面的程序设计实现,可以定义诸如方法拦截器和切入点等,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。

二、Ioc:

2.1spring Ioc容器(控制反转):

作用:减小类/程序间的耦合

个人理解:运行时,找对象的事变成了间接寻找。没有使用ioc时,是直接寻找,找不到,编译便报错,而使用后,spring会创建一个工厂,寻找的事交给了工厂来处理,由工厂来寻找所需要的类,这样便减少了类与类之间的耦合,进一步实现低耦合高内聚!但Ioc只能做到减小,无法做到真正的解耦。

2.2 两大容器:

2.2.1:BeanFactory 容器和 ApplicationContext 容器(常用):

BeanFactory 可以理解为含有bean集合的工厂类。而 ApplicationContext 是他的一个子接口(进行了扩展)。

BeanFactory 是一个基本的容器,他无法支持aop、web等功能。

BeanFactory 能做到的事, ApplicationContext 都能做到。

ApplicationContext: 单例对象适用 采用此接口

它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。

BeanFactory: 多例对象使用

它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。

2.2.2 ApplicationContext的三个常用实现类:

 ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(更常用)

 FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)

 AnnotationConfigApplicationContext: 读取注解创建容器

相关代码:
	ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
	xxx x = (xxx)ac.getBean("xxx"); //要强转类型
	xxx x = ac.getBean("xxx",xxx.class);

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

		<bean id="" class=""></beans>
		
	</beans>

2.3 spring对bean的管理细节

2.3.1 创建bean的三种方式

1.使用默认构造函数创建,在spring配置文件配id和class属性的bean
<bean id="" class=""></beans>

2.使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
<bean id="aaa" class="com.factory.aaa"></beans>
<bean id="bbb" factory-bean="aaa",factory=method=""getbbb></beans>

3.使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
<bean id="aaa" class="com.factory.StaticFactory" factory-method="getaaa"></beans>

2.3.2 bean对象的作用范围

scope属性: 
	*singleton  单例(默认)
	*prototype	多例
	*request	web应用的请求范围
	*session	web应用的会话范围
	*global-session:	作用于集群环境的会话范围

2.3.3 bean对象的生命周期

单例对象

出生:容器创建时

活着:容器在便在

死亡:容器销毁,死亡

多例对象

出生:当我们使用spring框架为我们创建(同beanFactory)

活着:对象在使用,就活着

死亡:等着java垃圾回收机制回收

2.4 依赖注入(DI)

1.能注入的数据:
	1.基本类型和String
	2.其他bean类型
	3.复杂类型/集合类型
		
2.注入方式:
	1.使用构造函数注入
		使用的标签:constructor-arg
		标签内的属性:
			type:指定注入的数据的数据类型,也是构造函数中某个或某些参数的类型
			index:用于指定注入的数据给构造函数中指定索引位置赋值
			name:用于指定给构造函数中指定名称的参数赋值
			value:提供基本类型和String类型的数据
			ref:指定其他的bean类型数据,即在Ioc核心容器中出现过的bean对象
	<bean id="" class="">
		<constructor-arg ></constructor-arg>
	</beans>
		
	2.使用set注入(常用)
		标签:property
		标签内属性:
			name:注入时所调用的set方法名称
			ref:指定其他的bean类型数据,即在Ioc核心容器中出现过的bean对象
		
	使用注解注入

2.5注解:

在bean.xml中要先表明扫描的包(要加新的约束)

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

创建对象:

	Component:将当前类对象存入spring容器,有个value属性:相当于bean的id(默认为当前类名第一个字母小写)
	以下三个与上面作用一模一样
		Controller:一般用于表现层
		Service:一般用于业务层
		Repository:一般用于持久层

注入数据:

	作用同bean标签的<property>
	1.Autowired:按照类型自动注入,此时set方法不为必须的了
		注入时,没有匹配类型则报错,如果多个匹配的类型,则以id来匹配
	2.Qualifier:按照类中注入的基础之上再按照名称注入,注入类成员不能单独食用,给	方法参数可以,属性value:用于指定注入bean的id。
	3.Resource:直接按照bean的id注入
	以上3个只能注入其他bean类型,基本类型和String不能,另外,集合类型只能通过xml实现
		
	4.Value:用于注入基本类型和String

指定作用范围:

	Scope:
		*singleton  单例
		*prototype	多例

配置类注解:

作用同bean.xml
	1.Congifuration:表面当前类是一个配置类
	2.ComponentScan:通过注解表明spring创建容器时要扫描的包
	3.Bean:用于把当前方法的返回值作为bean对象存入springIoc容器,属性name:指定bean的id,默认方法名称
	4.Import():  用于插入其他配置类
	5.PropertySource: 扫描的配置文件(一般是jdbc.properties)
	6.@EnableTransactionManagement:开启事务支持

2.6案例:

xml配置案例:

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

    <bean id="accountService" class="cn.zzz.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <bean id="accountDao" class="cn.zzz.dao.impl.AccountDaoImpl">
        <property name="runner" ref="runner"></property>
    </bean>

    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring01?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=UTC"></property>
            <property name="user" value="root"></property>
            <property name="password" value="1234"></property>
    </bean>
</beans>

AccountServiceImpl:

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public List<Account> findAllAccount() {
        return accountDao.findAllAccount();
    }

    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);
    }

    public void saveAccount(Account account) {
        accountDao.saveAccount(account);
    }

    public void updateAccount(Account account) {
        accountDao.updateAccount(account);
    }

    public void deleteAccount(Integer accountId) {
        accountDao.deleteAccount(accountId);
    }
}

AccountService:

public interface AccountService {

    /*
    查询所有
     */
    List<Account> findAllAccount();

    /*
    根据id查找
     */
    Account findAccountById(Integer accountId);

    /*
    保存
     */
    void saveAccount(Account account);

    /*
    更新
     */
    void updateAccount(Account account);

    /*
    删除
     */
    void deleteAccount(Integer accountId);
}

AccountDao:

public interface AccountDao {
    /*
    查询所有
     */
    List<Account> findAllAccount();

    /*
    根据id查找
     */
    Account findAccountById(Integer accountId);

    /*
    保存
     */
    void saveAccount(Account account);

    /*
    更新
     */
    void updateAccount(Account account);

    /*
    删除
     */
    void deleteAccount(Integer accountId);
}

AccountDaoImpl:

public class AccountDaoImpl implements AccountDao {
    private QueryRunner runner;

    public void setRunner(QueryRunner runner) {
        this.runner = runner;
    }

    public List<Account> findAllAccount() {
        try {
            return runner.query("select *from account",new BeanListHandler<Account>(Account.class));
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public Account findAccountById(Integer accountId) {
        try {
            return runner.query("select *from account where id = ?",new BeanHandler<Account>(Account.class),accountId);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public void saveAccount(Account account) {
        try {
             runner.update("insert into account(name,money) value (?,?)",
                     account.getName(),account.getMoney());
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public void updateAccount(Account account) {
        try {
            runner.update("update  account set name=?,money=? where id=?",
                   account.getName(),account.getMoney(),account.getId());
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public void deleteAccount(Integer accountId) {
        try {
            runner.update("delete from  account where id = ?",
                 accountId);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

Test:

public class TestCurd {
    @Test
    public void TestFindAll() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        AccountService as = ac.getBean("accountService",AccountService.class);
        List<Account> accounts = as.findAllAccount();
        for (Account account : accounts) {
            System.out.println(account);
        }
    }

    @Test
    public void TestFindOne() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        AccountService as = ac.getBean("accountService",AccountService.class);
        Account account = as.findAccountById(1);
        System.out.println(account);
    }

    @Test
    public void TestSaveAccount() {
        Account account = new Account();

        account.setName("老王");
        account.setMoney(1234f);

        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        AccountService as = ac.getBean("accountService",AccountService.class);

        as.saveAccount(account);
    }

    @Test
    public void TestUpdateAccount() {
        Account account = new Account();
        account.setId(1);
        account.setName("AAA");
        account.setMoney(1234f);
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        AccountService as = ac.getBean("accountService",AccountService.class);
        as.updateAccount(account);
    }

    @Test
    public void TestDeleteAccount() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        AccountService as = ac.getBean("accountService",AccountService.class);

        as.deleteAccount(4);
    }
}

注解配置:

SpringConfiguration:

/**
 * 相当于bean.xml
 */

@Configuration     //表明他是一个配置类,会被被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
@ComponentScan("cn.zzz")  //表面创建容器扫描的包
public class SpringConfiguration {

    @Bean("runner")
    @Scope("prototype")
    public QueryRunner runner(DataSource dataSource) {
        return new QueryRunner(dataSource);
    }

    @Bean("dataSource")
    public DataSource creatDatasource() {
        ComboPooledDataSource cbp = new ComboPooledDataSource();
        try {
            cbp.setDriverClass("com.mysql.cj.jdbc.Driver");
            cbp.setJdbcUrl("jdbc:mysql://localhost:3306/spring01?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC");
            cbp.setUser("root");
            cbp.setPassword("lin990306");
            return cbp;
        } catch (Exception e) {
           throw new RuntimeException(e);
        }
    }
}

AccountServiceImpl:

@Service("accountService")
public class AccountServiceImpl implements AccountService {
	
    @Autowired
    private AccountDao accountDao;


    public List<Account> findAllAccount() {
        return accountDao.findAllAccount();
    }

    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);
    }

    public void saveAccount(Account account) {
        accountDao.saveAccount(account);
    }

    public void updateAccount(Account account) {
        accountDao.updateAccount(account);
    }

    public void deleteAccount(Integer accountId) {
        accountDao.deleteAccount(accountId);
    }
}

AccountDaoImpl:

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {


    @Autowired
    private QueryRunner runner;



    public List<Account> findAllAccount() {
        try {
            return runner.query("select *from account",new BeanListHandler<Account>(Account.class));
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public Account findAccountById(Integer accountId) {
        try {
            return runner.query("select *from account where id = ?",new BeanHandler<Account>(Account.class),accountId);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public void saveAccount(Account account) {
        try {
             runner.update("insert into account(name,money) value (?,?)",
                     account.getName(),account.getMoney());
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public void updateAccount(Account account) {
        try {
            runner.update("update  account set name=?,money=? where id=?",
                   account.getName(),account.getMoney(),account.getId());
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public void deleteAccount(Integer accountId) {
        try {
            runner.update("delete from  account where id = ?",
                 accountId);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

其他与xml配置案例一样

三、Aop

AOP是Spring框架面向切面的编程思想,AOP采用一种称为“横切”的技术,将涉及多业务流程的通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置中。

相关术语:

Joinpoint(连接点): 所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的 连接点。

Pointcut(切入点): 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。

Advice(通知/增强): 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。 通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。

Introduction(引介): 引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方 法或 Field。

Target(目标对象): 代理的目标对象。

Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。

Proxy(代理): 一个类被 AOP 织入增强后,就产生一个结果代理类。

Aspect(切面): 是切入点和通知(引介)的结合

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


<dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.7</version>
    </dependency>

四:spring中的事务管理

题外:使用jdbcTemplate时,可以在dao中继承JdbcDaoSupport,便可以省去定义和注入,使用时直接super.getJdbcTemplate().方法。

基于xml的事务管理:
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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="accountService" class="cn.zzz.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <bean id="accountDao" class="cn.zzz.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>



    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/spring01?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=UTC"></property>
        <property name="username" value="root"></property>
        <property name="password" value="lin990306"></property>
    </bean>


    <!-- spring中基于XML的声明式事务控制配置步骤
        1、配置事务管理器
        2、配置事务的通知
                此时我们需要导入事务的约束 tx名称空间和约束,同时也需要aop的
                使用tx:advice标签配置事务通知
                    属性:
                        id:给事务通知起一个唯一标识
                        transaction-manager:给事务通知提供一个事务管理器引用
        3、配置AOP中的通用切入点表达式
        4、建立事务通知和切入点表达式的对应关系
        5、配置事务的属性
               是在事务的通知tx:advice标签的内部
     -->

    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置事务的通知-->
    <tx:advice id="txManager" transaction-manager="transactionManager">
        <!-- 配置事务的属性
               isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。
               propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。
               read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。
               timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
               rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。
               no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。
       -->
        <tx:attributes>
            <tx:method name="*" read-only="false" propagation="REQUIRED"/>
            <tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>

    <!--配置aop-->
    <aop:config>
    <!--建立切入点表达式-->
        <aop:pointcut id="pt1" expression="execution(* cn.zzz.service.impl.*.*(..))"/>
        <!--建立切入点表达式和事务通知的对应关系 -->
        <aop:advisor advice-ref="txManager" pointcut-ref="pt1"></aop:advisor>
    </aop:config>

</beans>
AccountServiceImpl:
public class AccountServiceImpl implements AccountService {

    private AccoundDao accountDao;

    public void setAccountDao(AccoundDao accoundDao) {
        this.accountDao = accoundDao;
    }

    public Account findAccoundByName(String AccountName) {
        return accountDao.findAccoundByName(AccountName);
    }

    public void updateAccount(Account account) {
        accountDao.updateAccount(account);
    }

    public List<Account> findAll() {
        return accountDao.findAll();
    }

    public void transfer(String outName, String inName, Float money) {
        Account outAccount = accountDao.findAccoundByName(outName);
        Account inAccount = accountDao.findAccoundByName(inName);

        outAccount.setMoney(outAccount.getMoney()-money);
        inAccount.setMoney(inAccount.getMoney()+money);

        accountDao.updateAccount(outAccount);

//        int i =1/0;
        accountDao.updateAccount(inAccount);
    }
}
Test:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {

    @Autowired
    private AccountService as;

    @Test
    public void TestFindAll(){
        List<Account> accounts = as.findAll();
        for (Account account : accounts) {
            System.out.println(account);
        }
    }

    @Test
    public void TestFindAccountByName(){

        Account account = as.findAccoundByName("AAA");
        System.out.println(account);
    }

    @Test
    public void TestTransfer(){

        as.transfer("AAA","bbb",100f);
    }
}

这种单元测试写法是为了让测试在Spring容器环境下执行。
posted @ 2020-02-08 16:13  lopzzzzzzzzzzzzzz  阅读(155)  评论(0编辑  收藏  举报