2019.6.18 spring的xml和注解

spring概要

bean对象的控制反转(IOC)和属性的依赖注入(DI)

spring中对于对象(bean)的三种配置:
(1)常见bean的无参构造的配置

<bean id ="user" class = "sprign.vo.User">
</bean>

(2)工厂类普通方法中创建的bean的配置

public class Bean3Factory {

	public Bean3 getBean3(){
		return new Bean3();
	}
}

<bean id="bean3Factory" class="cn.itcast.spring.demo3.Bean3Factory"></bean>

//注意创建上面bean3的实例的话,id标识要以下面bean3为准奥,上面那个id只是负责定位上面那个类,下面才是入口
//通过下面id确定工厂类的id名为bean3Factory,即定位到cn.itcast.spring.demo3.Bean3Factory类,并且定位
//到工厂中factory-method方法。最终就完成bean3对象的定位输出。
<bean id="bean3" factory-bean="bean3Factory" factory-method="getBean3"></bean>

(3)工厂类静态方法中创建bean的xml的配置

public class Bean2Factory {

	public static Bean2 getBean2(){
		return new Bean2();
	}
}

<!-- 方式二:静态工厂实例化 Bean -->
											 //被spring管理的类是工厂类,但该类方法下创建实例,所以要声明出方法
<bean id="bean2" class="cn.itcast.spring.demo3.Bean2Factory"  
factory-method="getBean2"/>

spring中对于对象(bean)内属性注入的四种情况:
(1)属性通过构造方式的注入,这情况下属性的标识符是 constructor-arg

	<bean id="car" class="cn.itcast.spring.demo4.Car">
		<constructor-arg name="name" value="保时捷"/>
		<constructor-arg name="price" value="1000000"/>
	</bean>

(2)属性通过set方式的注入,这情况下属性的标识符是 property

<bean id="user" class="spring.ov.User">
		 <property name="str" value="abc"></property>
		 <property name="sex" value="男"></property>
		
</bean>

(3)引用类型的属性的注入

//id 是该bean唯一身份标识奥!
<bean id="user1" class="spring.ov.User">
	<property name="age" value="22"></property>
	<property name="sex" value="女"></property>
</bean>

//person类中属性是引用类型,这时在标签property中设置引用参数和普通参数就不同了。
//普通参数是name=属性名称  value=该属性赋的值。
//引用参数是name = 属性名称  ref = 该引用类型对应的bean的id,告知spring去找到该类型的bean,才能帮创建对象奥!
<bean id = "person" class="spring.ov.person">
	<property name="name" value="person_user"></property>
	<property name="user" ref="user1"></property>
</bean>

(4)复杂类型的属性注入:集合等

<!-- Spring 的复杂类型的注入===================== -->
<bean id="collectionBean" class="cn.itcast.spring.demo5.CollectionBean">
   <!-- 数组类型的属性注入 -->
	<property name="arrs">
		<list>
			<value>会希</value>
			<value>冠希</value>
			<value>天一</value>
		</list>
	</property>
  <!-- List 集合的属性注入-->
	<property name="list">
		<list>
			<value>芙蓉</value>
			<value>如花</value>
			<value>凤姐</value>
		</list>
	</property>
  <!--  Map 集合的属性注入 -->
	<property name="map">
		<map>
			<entry key="aaa" value="111"/>
			<entry key="bbb" value="222"/>
			<entry key="ccc" value="333"/>
		</map>
	</property>
  <!-- Properties 类型的注入 -->
	<property name="properties">
		<props>
			<prop key="username">root</prop>
			<prop key="password">123</prop>
		</props>
	</property>
</bean>

控制反转(IOC)注解方式

注解的好处是免了在xml文件中手动注册bean,由spring框架自动向spring容器中注册bean。如@component及其下一些@service、@controllerspring容器中注入bean到变量中
上面是ioc的xml的配置,下面对于其注解的方式
(1)首先引入jar包,在原ioc六个包基础上多了个aop-jar包,因为注解就在里面。
(2)在类路径下创建applicationcontext.xml核心配置文件。(配置文件后,要引入约束,这样有提示)注解要引入context约束。
(3)下面准备类数据,这些都和xml配置文件一样,只是在核心配置文件中不用配置《bean id = “xx” class=“xxx”》了,首先配置注解扫描,用于告诉spring哪些包下哪些类是使用了注解,要你去扫描解析,而不是配置文件的解析(注解的方式其本质上相当于手动在配置文件中设置的作用是一样的。所以注解扫描就可以了,不用在配置文件中去查找bean或者property的配置了)。 就类似于开启了注解模式;如:

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

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//告知框架,在哪个包下的类都用注解解析。开启组件扫描模式。
<context:component-scan base-package="com.sun.annotaion"></context:component-scan>


</beans>

(4)下面就在类上通过注解将类交给spring就ok了(@Component(value=“mycar”) //相当于<bean id=“mycar” class=“被注解的类型”)注解本质也是将标识的目标装配到spring的核心配置文件中。类似于手动配置bean或者property一样。

@Component(value="mycar") //相当于<bean id="mycar" class="就是下面这个类">
public class mycar implements car {

	
	public void start() {
		System.out.println("我的车启动了1。。。");
		
	}
	public void run() {
		System.out.println("我的车上路了1。。。");
		
	}

	public void stop() {
		System.out.println("我的车熄火了1。。。");

	}

}

(5)上面就完成了控制反转,然后测试一下

public class car_test {

@Test
public void demo2() {
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext.xml");
	car car = (car)context.getBean("mycar");
	car.stop();
}
}

结果:
我的车熄火了1。。。

IOC的注解形式和详解

主要是 控制反转类 的 注解 和 属性 的注解
上面发现注解方式免了配置文件中各种设置了,方便了实际开发。但如果改动,还要改源代码,这是弊端。

  • 上面是对象的注解,那属性注入如何注解
  • 基本属性注入的注解——就是在属性上通过@value(“属性值”)就ok了,可以没有set方法奥!有set方法注解就写在方法上,没有就直接写在属性上
@component(类的注解

作用:就是修饰类的,将该类控制反转给spring(交给spring管理了)。
@component后期分三个:@controller、@service、@repository由于后期将针对不同层上的属性进行不同注解

属性注解
  1. 普通属性注解:就是@value(“值内容”)就ok了
  2. 引用类型注解: 可以通过@autowired(自动根据类型来依赖注入其内容的值);区别于以前在配置文件中按照名称name来注入。但是类型注入的话,有时候同一个接口类型的话,就不确定具体哪一个实现类的注入,所以常用@qualifier来明确具体类的id的方式来注入。
  3. 上面两个才实现了按名称的方式来注入的注解方式,有点不太方便,所以又出现一个便捷的@resource注解。实际开发中常用@resource(name=“”)按照名称来注入的注解方式。
    类似于< property name=“属性名” ref=“引用类的id名” > 属性注入这边,属性name开头可以不分大小写。
    (< property name=“age” value=“22”>< /property > name内容是类中属性名,开头不分大小写)
    案例:
@Component(value="sun_car")
public class sun_car implements car {

	@Override
	public void run() {
		System.out.println("第二辆车运行");
	}
	@Override
	public void start() {
		System.out.println("第二辆车开始");
	}
	@Override
	public void stop() {
		System.out.println("第二辆车停止");
	}
}
========================================
@Component(value="mycar") //相当于<bean id="mycar" class="就是下面这个类">
public class mycar implements car {
	@Value("sunxiang")
	private String name;
	
	@Resource(name="sun_car")
	private car car;	
	public void start() {
		System.out.println("2....."+car.getClass());
		car.start();
		System.out.println(name+"我的车启动了1。。。");		
	}
	public void run() {
		System.out.println(name+"我的车上路了1。。。");
	}

	public void stop() {
		System.out.println(name+"我的车熄火了1。。。");
	}
}
=============================================
public class car_test {
@Test
public void demo2() {
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext.xml");
	car car = (car)context.getBean("mycar");
	System.out.println("1......"+car.getClass());
	car.start();
}
}
--------------------
结果:
1......class com.sun.annotaion.mycar
2.....class com.sun.annotaion.sun_car
第二辆车开始
sunxiang我的车启动了1。。。
  1. bean生命周期的注解(作用于方法上的注解)——初始化注解:@PostConstruct(在作为初始化的方法上注解标识就ok了,没有其他内容奥!)。销毁注解:@PreDestroy(在某个作为销毁方法上面注解)
  2. bean的作用范围的注解(作用于类上的注解):@Scope(“singleton/prototype”)
@Test
public void demo2() {
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext.xml");
	car car1 = (car)context.getBean("mycar");
	System.out.println(car1);
	
	car car2 = (car)context.getBean("mycar");
	System.out.println(car2);
	}
	----------------------------------
	结果:       由于工厂类默认是单例模式创建bean
	com.sun.annotaion.mycar@1f1c7bf6
	com.sun.annotaion.mycar@1f1c7bf6
IOC的xml和注解的比较
应用场景不同

(1)xml:可以应用于任何场景、且结构清晰维护方便,直接可以通过xml配置文件可以了解类的整个结构(清晰类中用到哪些属性了,一目了然)。
(2)注解:如果以些类不是自己写的,直接打包成源码的那些,无法进入源码进行注解设置。好处是:开发方便。
(3)xml和注解结合使用:xml管理bean(整个结构中bean很清晰),注解注入属性(省去set方法,类中很清晰。)

<!-- 这个开启扫描的方式,是对于类上注解的应用奥! -->
<context:component-scan base-package="com.sun.annotaion"></context:component-scan>

<!-- 这个是没有用扫描的方式,这个只用于属性的注解的应用 。
就是类的关联由配置文件,属性通过注解形式,就用下面注解声明的-->
<context:annotation-config></context:annotation-config>

AOP基于aspectj注解方式

一.常见AOP的xml的方式
//下面的配置就是完成spring自动动态代理方式,由spring来完成方法的增强。
//注意虽然spring自动完成代理了,但不能忘记底层动态代理实现的原理奥!!!!!!!只对待增强的方法增强,其他原样输出

 <!-- 这个是目标对象的bean配置  -->
<bean id="mycar" class="spring.ov.mycar"></bean>  
 <!-- 这个是通知对象 的bean配置 -->
  <bean id="enhance" class="spring.ov.enhance"></bean>
  
 <!-- 下面才真正开始aop的配置 -->
 <aop:config >
 	<aop:pointcut expression="execution(* spring.ov.mycar.run(..))" id="pointcutid"/>
 	<aop:aspect ref="enhance" > 
 		<aop:before method="enhan" pointcut-ref="pointcutid"/>  
 								!!!!//前置通知。特点:可以在通知方法内获得切入点信息
 								!!!!!!//特别注意环绕通知,要将待增强方法传入环绕通知内中。
 	</aop:aspect>
 </aop:config>
二 AOP注解的前期准备

(1)同样引入jar包
在这里插入图片描述
(2)编写好目标类 和 切面类(通知类,里面是增强的方法)

//目标类
public class mycar  {
	public void start() {
		System.out.println("我的车启动了2。。。");	
	}
	public void run() {
		System.out.println("我的车上路了2。。。");		
	}
	public void stop() {
		System.out.println("我的车熄火了2。。。");
	}
}
====================================
//切面类(我习惯认为是通知类(增强方法的封装类))这个类很重要!!!!!!!
public class enhance {
public void enhan() {	
	System.out.println("加速度2。。。。");
}
}

(3)注意上面的类上没有注解,说明这边bean的管理没有使用注解管理方式

//这部分仅仅是试验AOP注解,所以IOC的注解没有使用。还是通过配置文件管理bean对象
<bean id ="mycar" class="com.sun.annotaion2.mycar"></bean>
<bean id = "enhance" class="com.sun.annotaion2.enhance"></bean>

(4)下面开始通过AOP注解来对目标类进行增强
同样和IOC注解那样,要在配置文件中(开启注解模式认证);ioc是开启IOC组件注解扫描,AOP这边要开启:


<!-- 在配置文件中开启AOP的注解模式 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

(5)开启了通过注解的方式来实现aop过程了,那么就可以直接通过注解来完成方法的增强了,不用配置文件中 <aop:config > </aop:config>内进行动态代理的各种配置了。
注意:!!! 前面分析过AOP面向切面编程(方法增强)中:两个关键点必须要明确

  • 哪个待增强类的哪个方法
  • 通知,就是增强的内容(方法)

这两个关键点都在切面类上体现

(6)注解符号:
@Aspect
@Before(value=“execution(表达式来表示哪个类的哪个方法)”)

(7)AOP注解入门测试:在切面上注解AOP的两个关键点:哪个类哪个方法要增强、通知(增强的内容/方法)

//通知类
@Aspect
public class enhance {
	
@Before(value="execution(* com.sun.annotaion2.mycar.run())")
public void enhan() {
	System.out.println("加速度2。。。。");
}
}

(8)AOP注解方式,只在切面类上注解设置,目标类上没有改变。即:

//目标类上和xml一样没有任何改动
public class mycar  {
	public void start() {
		System.out.println("我的车启动了2。。。");	
	}
	public void run() {
		System.out.println("我的车上路了2。。。");		
	}
	public void stop() {
		System.out.println("我的车熄火了2。。。");
	}
}

(9)注解方式的AOP实现的增强结果显示:最终完成对目标类mycar的run()方法的增强。源代码没任何改变。

public class car_test2 {
	@Test
	public void demo2() {	
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext2.xml");
	mycar car1 = (mycar)context.getBean("mycar");		
		car1.run();
	}
}
-------------------------------------
测试结果:
加速度2。。。。
我的车上路了2。。。

(10)测试类中可以结合spring的junit测试方式来表示:要引入spring-junit结合的jar包;对比上面测试代码,省略了工厂对象的创建。而且mycar的bean对象一属性注入的方式产生,不用写工厂对象的getbean获取,节省代码。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationcontext2.xml")
public class car_test2 {
	@Resource(name="mycar")
	private mycar car1;
@Test
public void demo2() {	
	car1.run();
}
}
-------------------------------------
测试结果:
加速度2。。。。
我的车上路了2。。。

对比下AOP的xml方式和注解方式:

  • 首先AOP关键点:切入点通知
  • 本质:要在切入点上添加通知;通俗说就是在哪个类的哪个方法上通过AOP方式进行增强。

中心思想:就是想在指定类的指定方法上进行增强。
(1)原始思想:创建类的时候,在方法中直接调用增强的方法。
(2)继承思想:将增强的方法封装,然后继承这个类,在方法中调用增强的方法,避免原始思想在所有要增强的类中都创建增强方法,然后调用增强方法。
(3)动态代理思想:只写一个动态代理类,然后传入要增强的类,并针对这个类中指定方法进行增强,任何增强的改变只在代理类中完成,不用像继承那样,增强方法的添加和删除的话,要所有类上都去掉继承。
(4)动态代理之后演进成AOP了(底层实现),接着又由Aspectj规范封装了,最后spring又引入Aspectj的aop,所有核心就是类似于动态代理奥!!!!所有出现两个关键点:哪个类的哪个方法要增强,增强的方法
(5)对于两个关键点:Spring的AOP的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"
     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
	http://www.springframework.org/schema/context 
	http://www.springframework.org/schema/context/spring-context.xsd
	http://www.springframework.org/schema/tx 
	http://www.springframework.org/schema/tx/spring-tx.xsd"> 


<bean id ="mycar" class="com.sun.annotaion2.mycar"></bean>
<bean id = "enhance" class="com.sun.annotaion2.enhance"></bean>

<aop:config>
//切入点,就是明确哪个类的哪个方法
<aop:pointcut expression="execution(* com.sun.annotaion2.mycar.run(..))" id="run1"/>  

<aop:aspect ref="enhance">
//明确增强的内容(方法)
<aop:before method="enhan" pointcut-ref="run1"/>
</aop:aspect>

</aop:config>
=========================================
//切面类就是基本的增强方法的封装
public class enhance {	
	public void enhan() {		
		System.out.println("加速度2。。。。");
	}
}

(6)对于Spring的AOP的注解体现: 对比配置文件形式,就是将aop:config</aop:config>内属性设置直接在切面类上注解设置(体现)了。还是设置两个关键点:具体哪个类的哪个方法,还有增强的内容(方法)

<!-- 在配置文件中开启AOP的注解模式 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

<bean id ="mycar" class="com.sun.annotaion2.mycar"></bean>
<bean id = "enhance" class="com.sun.annotaion2.enhance"></bean>

========================================
//注解主要是在切面类中注解设置的。
@Aspect
public class enhance {	
@Before(value="execution(* com.sun.annotaion2.mycar.run())")
public void enhan() {	
	System.out.println("加速度2。。。。");
}
}

(7)上面5和6,AOP的配置的测试结果都一样

package com.sun.annotaion2;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationcontext2.xml")
public class car_test2 {
	@Resource(name="mycar")
	private mycar car1;
@Test
public void demo2() {
	
	car1.run();
}
}
==================================
测试结果:
加速度2。。。。
我的车上路了2。。。

(8)同样后置通知配置的话,注解的话直接在通知方法上注解就可以了,不用到配置文件下那样配置。直接通知方法上@AfterReturning(value=“execution(* com.sun.annotaion2.mycar.run())”)就ok了。
环绕通知的注解和xml比较:

<aop:config>
<aop:pointcut expression="execution(* com.sun.annotaion2.mycar.run(..))" id="run1"/>
<aop:pointcut expression="execution(* com.sun.annotaion2.mycar.stop(..))" id="stop"/>
<aop:aspect ref="enhance">
<aop:before method="enhan" pointcut-ref="run1"/>
<aop:around method="around" pointcut-ref="stop"/>
</aop:aspect>
</aop:config>
========================================
@Aspect
public class enhance {
	
private Object proceed;
@Before(value="execution(* com.sun.annotaion2.mycar.run())")
public void enhan() {
	
	System.out.println("加速度2。。。。");
}
@AfterReturning(value="execution(* com.sun.annotaion2.mycar.run())")
public void after() {
	
	System.out.println("太快了,注意减速2。。。。");
}
@Around(value="execution(* com.sun.annotaion2.mycar.stop())")
//环绕通知相比于前置和后置,要特殊点,注解只在方法上注解,其他环绕方法内容和配置文件方式一样,没有改变
public Object around(ProceedingJoinPoint joinpoint) throws Throwable {
	System.out.println("环绕前。。。。。");
	proceed = joinpoint.proceed();
	System.out.println("环绕后。。。。。");
	return proceed;
}


/*
//环绕通知的注解要注意一下,原本没注解的方式也和其他前置后置不同
@Around("MyAspectAnno.pointcut3()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("环绕前通知==========");
Object obj = joinPoint.proceed();
System.out.println("环绕后通知==========");
return obj;
}
*/


}

小结

IOC注解中特别关注:类的注解(bean对象的管理) 类中属性的注解(属性注入)
AOP注解中特别关注:切入点(哪个类的哪个方法)的注解 通知的注解,都是在切面类上注解这两个关键信息的。特别是哪个类哪个方法别忘了,这表示通知要切入的目的位置奥,如前置注解:就是表示这个通知要切入的位置mycar类下的run()方法、之前切入

@Before(value="execution(* com.sun.annotaion2.mycar.run())")
public void enhan() {
	System.out.println("加速度2。。。。");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值