zoukankan      html  css  js  c++  java
  • Spring核心概念

    初始Spring

    在学习Spring之前让我们先了解一下企业级应用.企业级应用是指那些为商业组织,大型企业而创建并部署的解决方案及应用,这些大型企业级应用的结构复杂,涉及的外部资源众多,事务密集,数据规模大,用户数量多.有较强的安全性考虑和较高的性能要求,而Spring就是用来做解决这些问题的

    Spring是一个轻量级的框架,是当前的主流框架,它能使现有技术更加易用,推进编码最佳实践

    Spring坚持一个原则:不重新发名轮子 Spring由大约20个模块组成。
    分成六个部分:Core Container、DataAccess/Integration、Web、AOP、Instrumentation、Test。
    Spring Core是框架的最基础的部分,提供了IOC特性
    Spring Context为企业级开发提供了便利和集成的工具
    Spring AOP是基于Spring Core的符合规范的面向切面编程的实现。
    Spring JDBC提供了JDBC的抽象层,简化了JDBC编码
    Spring ORM对市面上流行的ORM框架提供了支持
    Spring Web 为Spring在Web应用程序中的使用提供了支持

    Spring IOC

    理解控制反转
    控制反转(Inversion of Control,IoC),也称为依赖注入(Dependency Injection,DI),是面向对象编程中的一种设计理念,用来降低代码之间的耦合度
    首先考虑什么是依赖,例如:在A类方法中,实例化了B类的对象并调用其方法以完成特定的功能,我们就
    说A类依赖于B类,下面我们通过示例理解一下.

    定义一个接口

    public interface UserDao{
        /**
        *保存用户信息的方法
        */
        public void save(User user);
    }
    

    接口的实现类

        pulbic class UserDaoImpl implements UserDao{
            public void save(User user){
                System.out.print("保存用户信息到数据库");
            }
        }
    

    用户的业务类

    public class UserServiceImpl implements UserService{
        //实例化所依赖的UserDao对象
        private UserDao dao=new UserDapImpl();
        //调用UserDao的方法保存用户信息
        dao.save(User);
    }
    

    以上代码所示,UserServiceImpl对UserDaoImpl存在依赖关系,这样的代码很常见,但是存在一个严重的问题,就是这个两个类是高度耦合,如果我们有新的需求需要替换UserDao的实现类,导致UserServiceImpl里面的代码也要随之发生改变,这种代码不具有优良的可扩展性和可维护性,甚至在开发中难以测试

    我们可以利用简单的工厂和工厂模式的思路解决此类问题

    public class UserDaoFactory{
        public static UserDao getInstance(){
            //具体实现过程省略
        }
    }
    
    public class UserServiceImpl implements UserService{
        //通过工厂获取所依赖的用户DAO对象
        private UserDao dao=UserDaoFactory.getInstance();
        public void addNewUser(User user){
            //调用用户DAO的方法保存用户信息
            dao.save(user);
        }
    }
    

    这里的用户DAO工厂类UserDaoFactory就体现了"控制反转的思想":UserServiceImpl不再依赖自身的代码去获得所依赖的具体DAO对象,而是把这一工作转交给了第三方--UserDaoFactory,从而避免了和具体UserDao实现类之间的耦合

    下面我们用Spring IOC 来实现

    首先我们要下载Spring的jar包
    jar包下载地址 我这边是用的版本是4.3.18
    解压后的文件几个重要文件夹的意思:
    docs:该文件夹下包含Spring的相关文档,包括API参考文档,开发手册
    libs:该文件夹下存放Spring各个模块的jar文件,每个模块均提供三项内容,开发所需的jar文件,以"-javadoc"后缀表示的API和以"-sources"后缀表示的源文件
    schema:配置Spring的某些功能时需要用到的schema文件,对于已经集成了Spring的IDE环境(如MyEclipse,IDEA),这些文件不需要专门导入

    我们将jar导入到项目中

    为项目添加log4j.properties文件,用来控制日志输出 文件放到resources里面

    # 下面的属性配置中,所有日志的输出级别是info,输出源是con 
    log4j.rootLogger=info,error,con
    #定义输出源的输出位置是控制台
    log4j.appender.con=org.apache.log4j.ConsoleAppender
    #定义输出日志的布局采用的类 
    log4j.appender.con.layout=org.apache.log4j.PatternLayout
    #定义日志输出布局
    log4j.appender.con.layout.ConversionPattern=%d{MM-dd HH:mm:ss}{%p}%c%n -%m%n
    

    编写HelloSpring类

    package cn.springdemo;
    
    /**
     * 第一个Spring,输出"Hello,Spring!"。
     * 
     * @author 安心
     */
    public class HelloSpring {
    	// 定义who属性,该属性的值将通过Spring框架进行设置
    	private String who = null;
    	/**
    	 * 定义打印方法,输出一句完整的问候。
    	 */
    	public void print() {
    		System.out.println("Hello," + this.getWho() + "!");
    	}
    	/**
    	 * 获得 who。
    	 * 
    	 * @return who
    	 */
    	public String getWho() {
    		return who;
    	}
    	/**
    	 * 设置 who。
    	 * 
    	 * @param who
    	 */
    	public void setWho(String who) {
    		this.who = who;
    	}
    }
    

    在resources下面创建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"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
    	http://www.springframework.org/schema/beans/spring-beans.xsd">
    	<!-- 通过bean元素声明需要Spring创建的实例。该实例的类型通过class属性指定,并通过id属性为该实例指定一个名称,以便在程序中使用 -->
    	<bean id="helloSpring" class="cn.springdemo.HelloSpring">
    		<!-- property元素用来为实例的属性赋值,此处实际是调用setWho()方法实现赋值操作 -->
    		<property name="who">
    			<!-- 此处将字符串"Spring"赋值给who属性 -->
    			<value>Spring</value>
    		</property>
    	</bean>
    </beans>
    

    在Spring配置文件中,使用元素来定义Bean(也称为组件)的实例,这个元素有两个常用属性:一个是id,表示定义的Bean实例的名称,另一个是class,表示定义的Bean实例的类型
    经验:
    (1) 使用元素定义一个组件时,通常需要使用id属性为其指定一个用来访问的唯一的名称。如果想为Bean指定更多的别名,可以通过name属性指定,名称之间使用逗号、分号或空格进行分隔
    (2) 在本例中,Spring为Bean的属性赋值是通过调用属性的setter方法实现的,这种做法称为“设值注入”,而非直接为属性赋值。若属性名为who,但是setter方法名为
    setSomebody(),Spring配置文件中应写成name="somebody"而非name="who"。所以在为属性和setter访问器命名时,一定要注意遵循JavaBean的命名规范。

    编写测试方法

     public void helloSpring() {
            // 通过ClassPathXmlApplicationContext实例化Spring的上下文
            ApplicationContext context = new ClassPathXmlApplicationContext(
                    "applicationContext.xml");
            // 通过ApplicationContext的getBean()方法,根据id来获取bean的实例
            HelloSpring helloSpring = (HelloSpring) context.getBean("helloSpring");
            // 执行print()方法
            helloSpring.print();
        }
    

    测试代码中的ApplicationContext是一个接口,负责读取Spring配置文件,管理对象的加载,生成,维护Bean对象与Bean对象之间的依赖关系,负责Bean的生命周期等,ClassPathXmlApplicationContext是ApplicationContext接口的实现类,用于从classpath路径中读取Spring配置文件

    Spring AOP

    理解面向切面
    面向切面编程(Aspect Oriented Programmin ,AOP)是软件编程发展到一定阶段的产物,AOP一般适用于具有横切逻辑的场合,如访问控制、事务管理、性能检测等。
    面向切面编程,简单的说就是在不改变原程序设计的基础上位代码段增加新的功能,对代码段进行增强处理。设计思想来源于代理设计模式。

    直接调用对象的方法

    在代理模式中可以为该对象设置一个代理对象,代理对象为fun()提供一个代理方法,当通过代理对象的fun()方法调用原方法的fun()方法时,就可以在代理方法中添加新的功能,也就是所谓的增强处理。
    通过代理对象调用方法

    AOP相关术语
    增强处理(Advice)
    前置增强
    后置增强
    环绕增强、异常抛出增强、最终增强等类型
    1、横切关注点
    对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
    2、切面(aspect)
    类是对物体特征的抽象,切面就是对横切关注点的抽象
    3、连接点(joinpoint)
    被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
    4、切入点(pointcut)
    对连接点进行拦截的定义
    5、通知(advice)
    所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
    6、目标对象
    代理的目标对象
    7、织入(weave)
    将切面应用到目标对象并导致代理对象创建的过程
    8、引入(introduction)
    在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

    下面我们写一个使用Spring AOP实现日志输出也就是我们的log4j
    1.我们先在项目中加入jar包
    spring-aop 这个jar包 这个jar包给我们提供了AOP的实现,同时,Spring AOP还依赖AOP Alliance(aopalliance-1.0.jar)和AspectJ(aspectjweaver-1.6.9.jar)项目中的组件

    接下来编写业务类UserServiceImpl

    
    /**
     * 用户业务类,实现对User功能的业务管理
     */
    public class UserServiceImpl implements UserService {
    
    	// 声明接口类型的引用,和具体实现类解耦合
    	private UserDao dao;
    
    	// dao 属性的setter访问器,会被Spring调用,实现设值注入
    	public void setDao(UserDao dao) {
    		this.dao = dao;
    	}
    
    	public void addNewUser(User user) {
    		// 调用用户DAO的方法保存用户信息
    		dao.save(user);
    	}
    }
    

    该项目中有一个addNewUser()方法,实现添加用户的业务,接下来就以AOP的方式为该方法添加日志功能
    编写增强类

    public class UserServiceLogger {
    	private static final Logger log = Logger.getLogger(UserServiceLogger.class);
    
    	public void before(JoinPoint jp) {
    	    log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
    	            + " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
    	}
    	
    	public void afterReturning(JoinPoint jp, Object result) {
    	    log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
    	            + " 方法。方法返回值:" + result);
    	}
    }
    

    before()方法也就是在我们代码执行之前运行,afterReturning()在我们执行代码之后运行.Join Point类型的参数,Spring会自动注入该实例,通过getTarget()方法可以得到被代理的目标对象,getSignature()方法返回被代理的目标方法,getArgs()方法返回传递给目标方法的参数数组

    在Spring配置文件中对相关组件进行声明

    <bean id="dao" class="dao.impl.UserDaoImpl"> </bean>
    <bean id="service" 
    class="service.impl.UserServiceImpl">
        <property name="userDao" ref="dao"></property>
    </bean>
    <bean id="theLogger" class="aop.UserServiceLogger"></bean>
    

    接下来在Spring配置文件中进行AOP相关的配置,首先定义切入点
    定义切入点

    <?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">
        <bean id="dao" class="com.jikedaquan.spring.dao.impl.UserDaoImpl"> </bean>
        <bean id="service" class="service.impl.UserServiceImpl">
            <property name="userDao" ref="dao"></property>
        </bean>
        <bean id="theLogger" class="aop.UserServiceLogger"></bean>
        <aop:config>
            <!--定义一个切入点表达式,并命名为“pointcut”-->
            <aop:pointcut id="pointcut" 
            expression="execution(public void addNewUser(entity.User))"/> 
        </aop:config>
    </beans>
    

    与AOP相关的配置都放在aop:config标签中,如配置切入点的标签aop:pointcut
    execution是切入点指示符,切入点表达式支持模糊匹配

    • public * addNewUser(entity.User):* 表示匹配所有类型的返回值
    • public void * (entity.User):* 表示匹配所有方法名。
    • public void addNewUser(..):.. 表示匹配所有参数个数和类型
      • com.service.* .*(..):匹配com.service包下所有类的所有方法
      • com.service..* .(..):匹配com.service包及其子包下所有类的所有方法 (前面句号就是 不知道为啥 *显示不出来 应该是Markdown的某个命令符号把)

    大家可以根据自己的需求来设置切入点的匹配规则,当然配置的关键字还有很多,我就不一一介绍了,如果有兴趣可以查看Spring的开发手册
    最后还需要在切入点处插入增强处理,这个过程的专业叫法是”织入“

    <?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">
        <bean id="dao" class="com.jikedaquan.spring.dao.impl.UserDaoImpl"> </bean>
        <bean id="service" class="service.impl.UserServiceImpl">
            <property name="userDao" ref="dao"></property>
        </bean>
        <bean id="theLogger" class="aop.UserServiceLogger"></bean> 
        <aop:config>
            <!--定义一个切入点表达式,并命名为“pointcut”-->
            <aop:pointcut id="pointcut" expression="execution(public void addNewUser(entity.User))"/>
            <!--引用包含增强方法的Bean-->
            <aop:aspect ref="theLogger">
                <!--将before()方法定义为前置增强并引用pointcut切入点-->
                <aop:before method="before" pointcut-ref="pointcut"></aop:before>
                <!--将afterReturning()方法定义为后置增强并引用pointcut切入点-->
                <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"></aop:after-returning>
            </aop:aspect>  
           </aop:config>
    </beans>
    

    编写测试

    ApplicationContext context=new 
    ClassPathXmlApplicationContext("applicationContext.xml");
    UserService service= (UserService) context.getBean("service");
    User user =new User();
    service.addNewUser(null);
    

    下一篇文章还有很详细的讲解IOC和AOP一些功能,不足的地方欢迎大家提出意见
    by安心

  • 相关阅读:
    【BZOJ2734】【HNOI2012】集合选数(状态压缩,动态规划)
    【Luogu1879】玉米田(状态压缩,动态规划)
    【BZOJ1911】【APIO2010】特别行动队(斜率优化,动态规划)
    蒟蒻关于斜率优化DP简单的总结
    【BZOJ1010】【HNOI2008】玩具装箱(斜率优化,动态规划)
    【BZOJ4196】【NOI2015】软件包管理器(树链剖分,线段树)
    【BZOJ1483】【HNOI2009】梦幻布丁(启发式合并,平衡树)
    【BZOJ1058】【ZJOI2007】报表统计(链表,堆,Splay)
    【BZOJ1012】【JSOI2008】最大数(线段树)
    【SHOI2012】魔法树(树链剖分,线段树)
  • 原文地址:https://www.cnblogs.com/anxin0/p/9935146.html
Copyright © 2011-2022 走看看