spring小结一

一、概述
    1. Spring是一个开源框架,其目的是为了解决企业应用开发的复杂性.
    2. Spring功能:使用基本的JavaBean代替EJB,并提供更多的企业应用功能.
    3. 简单说,Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架.
二、Spring的jar包和配置文件
    1.Spring的jar包
        (1)spring的核心类库在spring文档的dist下.
        (2)引入的第三方类库在spring文档的lib下.
        (3)常用第三方类库
            * 如果使用了切面编程(AOP),需要    lib/aspectj/aspectjweaver.jar和aspectjrt.jar
                                    lib/cglib/cglib-nodep-2.1_3.jar
            * 如果使用了@Resource/@PostConstruct/@PreDestroy等注解,需要
                    lib/j2ee/common-annotations.jar
    2.Spring的配置文件
        默认是applicationContext.xml文件.
        但实际工程中,一般建立多个xml文件,在applicationContext.xml中引入.
三、Spring的基本功能
    1.SpringIOC
        * spring的控制反转:把对象的创建、初始化、销毁等工作交给spring容器来做,由spring容器控制对象的生命周期.                
        * spring容器的启动:
            (1)在类路径下寻找配置文件来实例化容器(常用):
                ApplicationContext ac = new ClassPathXmlApplicationContext(new String[]{"beans.xml"});
                这种方式需要将spring配置文件放到当前项目的classpath路径下.
            (2)在文件系统路径下寻找配置文件来实例化容器:
                ApplicationContext ac = new FileSystemXmlApplicationContext(new String[]{"d:\\beans.xml"});
            * 配置文件可以有多个,通过String数组传入.
    2.别名
        * 使用别名可以达到,在一个地方命名,多个地方使用不同名字的效果.
        <beans>
            <alias name="person" alias="p"/>
            <bean name="person" class="com.liu.domain.Person"/>
        </beans>
    3.Spring容器内部对象的创建
        (1)使用类构造器实例化(默认是无参数的)    
            <bean id="personService" class="com.liu.bean.impl.PersonServiceImpl"/>
        (2)使用静态工厂方法实例化(简单工厂模式)
            <bean id="personService" class="com.liu.factory.PersonServiceFactory" factory-method="createPersonService"/>
            工厂类:
                public  class PersonServiceFactory {
                    public static PersonService createPersonService(){
                        return new PersonServiceImpl();
                    }
                }
        (3)注意,初始化bean时机
            * Spring默认在启动时将所有singleton bean提前进行实例化.即作为初始化的一部分.
            * ApplicationContext会自动创建并配置所有的singleton bean.
            * Lazy-init="false"时,spring容器将在启动的时候报错(可以及时发现错误)
            * Lazy-init="true"时,spring容器将在调用该类的时候出错.
    4.Bean的作用域(scope属性)
        (1)singleton(默认值)
            * 在每个spring IoC容器中,一个bean定义只有一个对象实例,即是单例的.
            * 默认情况下Bean节点的lazy-init为false,即容器启动就初始化bean,如果想延迟,可改为true
            * 如果相对所有bean都延迟初始化,可以在根节点Beans设置default-lazy-init="true"
                例如:<beans default-lazy-init="true"..>
        (2)prototype
            * 允许bean可以被多次实例化(使用一次就创建一个实例).
            * spring不能对prototype bean的整个生命周期负责,这就意味着清楚prototype作用域的对象
                并释放任何prototype bean所持有的昂贵资源都是客户端的责任        
        (3)Request
            在一次Http请求中,一个bean定义对应一个实例;
            即每次Http请求将会有各自的bean实例,它们依据某个bean定义创建而成.
            该作用域仅在基于web的Spring ApplicationContext情形下有效.
        (4)Session
            在一个Http Session中,一个bean定义对应一个实例,该作用域仅在基于web的Spring ApplicationContext情形下有效.
        (5)Global session
            再一个全局的Http Session中,一个bean定义对应一个实例.
            典型情况下,仅在使用portlet context的时候有效.
            该作用域仅在基于web的Spring ApplicationContext情形下有效.
        (6)指定Bean的初始化方法和销毁方法
             * Spring初始化bean或销毁bean时,有时需要作一些处理工作,因此spring可以在创建和拆卸bean的时候调用bean的两个生命周期方.
                  <bean id=“foo” class=“...Foo”
                        init-method=“setup”
                        destory-method=“teardown”/>
            * 当foo被载入到Spring容器中时调用init-method方法.当foo从容器中删除时调用destory-method(scope = singleton有效)
    5.依赖注入(DI)
        (1)使用构造器注入
            * 通过xml的方式注入:
                A:通过参数的顺序:
                    <constructor-arg index="0">
                        <value>张三</value>
                    </constructor-arg>
                    <constructor-arg index="1">
                        <value>56</value>
                    </constructor-arg>
                B:通过参数的类型:
                    <constructor-arg type="java.lang.Integer">
                        <value>56</value>
                    </constructor-arg>
                    <constructor-arg type="java.lang.String">
                        <value>张三</value>
                    </constructor>
        (2)使用属性setting方法进行注入
            * 通过xml的方式注入:
                A:简单Bean的注入:
                    简单Bean包括:包装类型和String
                        <bean id="personService" class="com.liu.service.impl.PersonServiceImpl">
                            <!-- 基本类型,String类型 -->
                            <property name="age" value="20"/>
                            <property name="name" value="张三"/>
                        </bean>
                B:引用其它Bean
                    <bean id="person" class="com.itcast.bean.Person" />
                    <bean id="personService"  class="com.itcast.bean.impl.PersonServiceImpl">
                        <property name="person" ref="person" />
                    </bean>
        (3)装配list集合
            <property name="lists">
                <list>
                    <value>list1</value>
                    <value>list2</value>
                </list>
            </property>
        (4)装配set集合
            <property name="sets">
                <set>
                    <value>set1</value>
                    <value>set2</value>
                </set>
            </property>
        (5)装配map
            <property name="maps">
                <map>
                    <entry key="01">
                        <value>map01</value>
                    </entry>
                    <entry key="02">
                        <value>map02</value>
                    </entry>
                </map>
            </property>
        (6)装配Properties
            <property name="props">
                <props>
                    <prop key="01">prop1</prop>
                    <prop key="02">prop2</prop>
                </props>
            </property>
    6.注解注入    
        (1)步骤
            A.在配置文件中,引入context命名空间(星号的)
                <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-2.5.xsd
                *****   http://www.springframework.org/schema/context
                *****   http://www.springframework.org/schema/context/spring-context-2.5.xsd">
            B.在配置文件中加入context:annotation-config标签
                <context:annotation-config/>
                此配置隐式注册了多个对注释进行解析处理的处理器.
                AutowiredAnnotationBeanPostProcessor,
                CommonAnnotationBeanPostProcessor,
                PersistenceAnnotationBeanPostProcessor,
                RequiredAnnotationBeanPostProcessor
                注:@Resource注解在spring文档的lib\j2ee\common-annotations.jar    
        (2)@Autowired
            * @Autowired与@Resource区别:
                @Autowired默认按类型装配
                @Resource默认按名称装配,当找不到与名称匹配的bean时,才会按类型装配.
            * @Autowired默认情况下要求依赖对象必须存在,如果允许为null值,可设置它required属性为false
                    @Autowired(required=false)
                    private PersonDao personDao; //用于字段上
                    @Autowired(required=false)
                    public void setPersonDao(PersonDao personDao){ //用于属性set方法上
                        this.personDao = personDao;
                    }
        (3)@Qualifier
            如果向按名称装配,可以结合@Qualifier注解一起使用.
                    @Autowired@Qualifier("personDao")
                    private PersonDao personDao; //用于字段上
                    @Autowired
                    public void setPersonDao(@Qualifier("personDao")PersonDao personDao){
                        this.personDao = personDao;
                    }        
        (4)@Resource
            * 它也可标注在字段或属性的setter方法上.
            * 如果没有指定name属性,会先按照名称寻找依赖对象,如果找不到,会退回到按类型装配,而指定了name属性,则只能按名称装配.
        (5)@PostConstruct和@PreDestroy
                @PostConstruct:指定bean的初始化方法.
                @PreDestroy:指定bean的销毁方法.
    7.扫描注入
        在一个稍大的项目中,通常会有上百个组件,如果这些组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不太方便.
        spring2.5为我们引入了组件自动扫描机制,它可以在类路径底下寻找标注了@Component、@Service、
        @Controller、@Repository注解的类,并把这些类纳入进spring容器中管理.
        (1)    步骤
            * 引入context命名空间,
                和注解注入引入的命名空间是一样的.
            * 在配置文件中添加context:component-scan标签
                <context:component-scan base-package="com.liu"/>
                其中base-package为需要扫描的包(包含子包)
        (2)功能
            @Service 用于标注业务层组件
            @Controller 用于标注控制层组件(如struts中的action)    
            @Repository 用于标注数据访问组件,即DAO组件
            @Component 泛指组件,当组件不好归类时,可以用这个注解标注.
    8.spring中的继承
        Person类:
            public class Person{
                private String sex;
                //..setter,getter方法略
            }        
        student类:
            public class Student extends Person{
                ..
            }
        配置文件:
            <bean id="person" class="com.liu.Person">
                <property name="sex">
                    <value>man</value>
                </property>
            </bean>
            <bean id="student" class="com.liu.Student" parent="person"/>
        配置文件中,parent属性为student在容器中继承person,如果去掉person是不行的.    
四、面向切面编程            
    1.代理模式

        (1)JDK动态代理

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKProxy implements InvocationHandler {
	private Object targetObject; //代理的目标对象

	
	public Object createProxyInstance(Object targetObject) {
		this.targetObject = targetObject;
		//第一个参数设置代码使用的类加载器,一般采用跟目标类相同的类加载器
		//第二个参数设置代理类实现的接口跟目标类使用相同的接口
		//第三个参数设置回调对象,当代理对象的方法被调用时,会调用该参数指定对象的invoke方法
		return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
				targetObject.getClass().getInterfaces(), this);
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("代理类实例" + proxy.getClass());
		System.out.println("方法名称" + method.getName());
		if(args!=null && args.length>0) { //方法的参数值
			for(int i=0; i<args.length; i++) {
				System.out.println("方法参数值" + args[i].toString());
			}
			Object returnvalue = null; //定义方法的返回值,没有返回值时是null
			returnvalue = method.invoke(this.targetObject, args);//调用目标对象的方法
			System.out.println("方法的返回值" + returnvalue);
			return returnvalue;
		}
		return null;
	}
}

            总结:jdk动态代理必须具备四个条件:目标接口、目标类、拦截器、代理类
                * 因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有.
                * 利用JDKProxy方式必须有接口的存在
                * invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型.
        (2)CGLIB做代理
            * 用CGlib生成代理类是目标类的子类
            * 用CGlib生成代理类不需要接口
            * 用CGlib生成的代理类重写了父类的各个方法
            * 拦截器中的intercept方法内容正好就是代理类中的方法体

	//代码示例:需要添加cglib的jar包,可以在spring文档的lib下找到
	import java.lang.reflect.Method;
	import net.sf.cglib.proxy.Enhancer;
	import net.sf.cglib.proxy.MethodInterceptor;
	import net.sf.cglib.proxy.MethodProxy;
	
	public class CglibProxy implements MethodInterceptor{
		private Object targetObject; //代理的目标对象
		
		//创建目标对象的代理对象
		public Object createProxyInstance(Object targetObject) {
			this.targetObject = targetObject;
			Enhancer enhancer = new Enhancer(); //该类用于生成代理对象
			enhancer.setSuperclass(this.targetObject.getClass()); //设置父类
			enhancer.setCallback(this); //设置回调对象为本身
			return enhancer.create(); //创建代理对象
		}
		/* obj 目标对象代理类的实例
		 * method 代理实例上调用父类方法的Method实例
		 * args 传入到代理实例上方法参数值的对象数组
		 * methodProxy 使用它调用父类的方法
		 */
		@Override
		public Object intercept(Object obj, Method method, Object[] args,
				MethodProxy methodProxy) throws Throwable {
			System.out.println("代理类" + obj.getClass());
			System.out.println("方法名称" + method.getName());
			if(args!=null && args.length>0) {//方法的参数值
				for(int i=0; i<args.length; i++) {
					System.out.println("方法参数" + args[i]);
				}
				Object returnvalue = null;//方法的返回值,无返回类型时,为null
				returnvalue = methodProxy.invoke(this.targetObject, args);//调用目标对象的方法
				System.out.println("方法的返回值" + returnvalue);
				return returnvalue;
			}
			return null;
		}
	}

        (3)spring的两种代理方式(默认是jdk动态代理)
            * 若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理.
                优点:因为有接口,所以使系统更加松耦合
                缺点:为每一个目标类创建接口
            * 若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类
                优点:因为代理类与目标类是继承关系,所以不需要有接口的存在.
                缺点:因为没有使用接口,所以系统的耦合性相对要差.
    2.AOP编程
        (1)概念
            * Aspect(切面):比如说事务、权限等,与业务逻辑没有关系的部分.
            * joinpoint(连接点):目标类的目标方法,由客户端在调用的时候决定
            * Pointcut(切入点):指我们要对那些拦截的方法的定义,被纳入spring aop中的目标类的方法
            * Advice(通知):指拦截到joinpoint之后要做的事情就是通知.
                            分为,前置通知、后置通知、异常通知、最终通知、环绕通知.
            * Target(目标对象):代理的目标对象
            * Weaving(织入)pid=11&gid=1:指把切面应用到目标对象来创建新的代理对象的过程.
                            切面在指定的连接点织入到目标对象.
        (2)对应JDKProxy代理中的概念
                SpringAop        JDKProxy代理
                    目标对象            目标对象
                    切面                拦截器类
                    通知                拦截器类中的方法
                    切入点            被拦截到的目标类中方法的集合
                    连接点            在客户端调用的方法(目标类目标方法)
                    AOP代理            代理类
                    织入                代理类的代理方法生成的过程
        (3)AOP实现的两种模式
            * xml形式

<!-- 在spring中配置文件中声明切面 -->       	
<bean id="security" class="com.liu.aop.before.Security" />
   
<!-- 在spring中配置文件中声明目标类 -->	
<bean  id="userManager" class="com.liu.aop.before.UserManagerImpl"/>

<!-- 定义切面、切入点、通知-->
<aop:aspect id="securityid" ref="security">
	<aop:pointcut id="perform" expression="execution(* com.liu.aop.before.UserManagerImpl.save*(..))"/>
	<!--定义前置通知-->	
	<aop:before method="checkSecurity" pointcut-ref="perform" />
</aop:aspect>

<!--定义切入点 ,让通知拦截到com.liu.aop.before.UserManagerImpl中以save开始的方法
	* 使用切入点指示符
		execution : 匹配方法执行的连接点,这是你将会用到的Spring的最主要的切入点指示符。
	* execution切入点指示符表达式如下:
		例如:execution("public * com.liu.UserManagerImpl.*(..) throws Exception")
			execution(	modifiers-pattern? 	方法的修饰符模式:不是必须的
						ret-type-pattern:	返回类型模式:必须的
						declaring-type-pattern?: 路径模式,不是必须的.
										* 方法所在类的路径
						name-pattern:方法的名称 必须的
									*表示类中所有的方法
									"save*"表示类中以save开始的方法
						(param-pattern):参数模式,必须的
									* ():匹配了一个不接受任何参数的方法
									* (..):匹配了一个接受任意数量参数的方法.
									* 模式(*)匹配了一个接受一个任何类型的参数的方法
									* 模式(*,String)匹配了一个接受两个参数的方法,
											第一个可以是任意类型,第二个必须是String类型
						throws-pattern? : 异常模式,抛出的异常  不是必须的
	示例:
		* 任意公共方法的执行:execution(public * * (..))
		* 任何一个名字以set开始的方法的执行:execution(* set*(..))
		* AccountService接口定义的任意方法的执行:execution(* com.liu.service.AccountService.*(..))
		* 在service包中定义的任意方法的执行:execution(* com.liu.service..*(..))
		* 在service包或其子包中定义的任意方法的执行:execution(* com.liu.service..*.*(..))
 -->

                * 前置通知:
                    <aop:before method="checkSecurity" pointcut-ref="perform" />                
                    * method="checkSecutiry":表示把切面(security)中checkSecurity方法定义为前置通知
                    * pointcut-ref="perform":把切入点应用到通知.
                    * 前置拦截的类必须实现MethodBeforeAdvice接口,实现其中的before方法
                * 后置通知:
                    <aop:after-returning method="checkSecurity" pointcut-ref="perform" returning="val" />         
                    * return="val" 通知里可以有返回参数,这个参数只能决定通知里能不能拿到方法的返回值,和客户端没关系.
                    * 在执行目标类的目标方法中遇到异常,则不执行后置通知.
                    * 后置拦截的类必须实现AfterReturningAdvice接口,实现其中的afterReturning方法.
                    * 在拦截器中的方法要和checkSecurity方法一样,有两个参数:
                                JoinPoint point : 可以获得目标方法和参数值
                                Object val :这里的名字要和return="val"中保持一致,指的是方法的返回值
                * 最终通知:
                    <aop:after method="checkSecurity" pointcut-ref="perform" />
                    * 注意:最终通知不受异常影响,即不论目标方法执行的过程中是否抛出异常,最终通知都将执行.
                * 环绕通知:
                    <aop:around method="checkSecurity" pointcut-ref="perform" />
                    * 前后拦截的类必须实现MethodInterceptor接口,实现其中的invoke方法.
                * 异常通知:
                    <aop:after-throwing method="checkSecurity" pointcut-ref="perform" throwing="ex"/>    
                    * 其中throwing指定了传递异常的参数名称
                    * 在异常通知中(拦截器)中,必须是checkSecurity方法
                        方法中有两个参数:
                                JoinPoint point:可以获得方法的名称、参数
                                Throwable ex : 利用ex.getMessage()可以获得异常信息
            * 注解形式
                为了在spring配置中使用@AspectJ切面,首先必须启用Spring对@AspectJ切面配置的支持,并确保自动代理

	<beans ...>
		<!-- 启用spring对@AspectJ的支持 -->
		<aop:aspectj-autoproxy/>
		<!-- 声明切面对象 -->
		<bean id="security" class="com.liu.service.Security"/>
		<!-- 创建接口实现类对象 -->
		<bean id="userManager" class="com.liu.service.UserManagerImpl"/>
	</beans>
	// 前置通知
		/* @Aspectj是按照类型匹配
		 * @Pointcut用于声明切入点
		 *	在@AspectJ注解风格的aop中,一个切入点签名通过一个普通的方法来定义
		 * 		1.作为切入点签名的方法必须返回void类型
		 *		2.方法没有参数用private修饰
		 *		3.方法体为空
		 *	切入点表达式的写法
		 *	execution(..)表示匹配方法执行的连接点
		 *		1."*"表示方法的返回类型任意
		 * 		2.com.liu.service..* 表示service包及其子包中所有的类
		 * 		3.save* 表示类中所有以save开头的方法
		 * 		4.(..)表示参数是任意数量
		 *	@Before 前置通知,在方法调用前执行
		 *	userManagerPointcut() 表示前面定义的切入点
		 *	args 限定匹配特定的连接点(使用spring AOP 的时候方法的执行),其中参数是指定类型的实例
		 *  username1,psw 参数表示指定类型的实例
		 *		参数名称必须与通知的参数名称一致
		 *		通知参数的类型必须与目标对象参数的类型一致,如不一致,则拦截不到	
		 */
		@Aspect
		public class Security {
			@Pointcut("execution(* com.liu.service..*.save*(..))")
			private void userManagerPointcut(){}
			
			@Before("userManagerPointcut() && args(username1,psw)")
			public void checkSecurity(String username1, String psw){
				System.out.println("安全检查"+username1+" "+psw);
			}
		}
	// 后置通知
		@Aspect
		public class Security {
			@Pointcut("execution(* com.liu.service..*.save*(..))")
			private void userManagerPointcut(){}
			
			@AfterReturning(pointcut="userManagerPointcut()",returning="value")
			public void checkSecurity(String value) {
				System.out.println("安全检查"+value);
			}
		}
	// 异常通知
		@Aspect
		public class Security {
			@Pointcut("execution(* com.liu.service..*.save*(..))")
			private void userManagerPointcut(){}
			
			@AfterThrowing(pointcut="userManagerPointcut()",throwing="ex")
			public void checkSecurity(Exception ex) {
				System.out.println("安全检查" + ex);
			}
		}
	// 最终通知
		@Aspect
		public class Security {
			@Pointcut("execution(* com.liu.service..*.save*(..))")
			private void userManagerPointcut(){}
			
			@After("userManagerPointcut()")
			public void checkSecurity(Exception ex) {
				System.out.println("安全检查");
			}
		}
	// 环绕通知
		@Aspect
		public class Security {
			@Pointcut("execution(* com.liu.service..*.save*(..))")
			private void userManagerPointcut(){}
			
			@Around("userManagerPointcut()")
			public Object checkSecurity(ProceedingJoinPoint point)throw Throwable {
				System.out.println("方法的名称" + point.getSignature().getName());
				System.out.println("测试参数对象是否为空" + point.getArgs().toString());
				if(point.getArgs().length > 0) {
					for(int i=0; i<point.getArgs().length; i++) {
					System.out.println("方法的参数值" + point.getArgs()[i]);
					}
				}
				Object returnvalue = null; //调用方法的返回值,方法无返回值时是null
				returnvalue=point.proceed(); //调用目标对象方法
				System.out.println("returnvalue" + returnvalue);
				System.out.prntln("进行安全检查");
				return returnvalue;
			}
		}


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值