[关闭]
@adamhand 2019-02-15T17:34:53.000000Z 字数 21004 阅读 747

Spring


Spring概念

spring是开源的轻量级框架

spring核心主要两部分:

  • aop:面向切面编程,扩展功能不是修改源代码实现
  • ioc:控制反转:对象的创建不是通过new方式实现,而是交给spring配置创建类对象

spring是一站式框架,spring在javaee三层结构中,每一层都提供不同的解决技术

  • web层:springMVC
  • service层:spring的ioc
  • dao层:spring的jdbcTemplate

IOC(DI)

IOC和DI的概念和理解

IOC(DI):通常,每个对象在使用他的合作对象时,自己均要使用像new object() 这样的语法来完成合作对象的申请工作,比如在A类的对象中要使用B类的对象,就要通过new方法创建一个B类对象。这样,对象间的耦合度高了。而IOC的思想是:Spring容器来实现这些相互依赖对象的创建、协调工作。对象只需要关系业务逻辑本身就可以了。从这方面来说,对象如何得到他的协作对象的责任被反转了(IOC、DI)。

DI其实就是IOC的另外一种说法。DI是由Martin Fowler 在2004年初的一篇论文中首次提出的。他总结:控制的什么被反转了?就是:获得依赖对象的方式反转了。

下面通俗一点理解IOC和DI:

IOC原理

Spring中的IoC的实现原理就是工厂模式加反射机制

工厂模式和反射

先来看一下工厂模式。下面是不使用反射的工厂模式。

  1. interface fruit{
  2. public abstract void eat();
  3. }
  4. class Apple implements fruit{
  5. public void eat(){
  6. System.out.println("Apple");
  7. }
  8. }
  9. class Orange implements fruit{
  10. public void eat(){
  11. System.out.println("Orange");
  12. }
  13. }
  14. //构造工厂类
  15. //也就是说以后如果我们在添加其他的实例的时候只需要修改工厂类就行了
  16. class Factory{
  17. public static fruit getInstance(String fruitName){
  18. fruit f=null;
  19. if("Apple".equals(fruitName)){
  20. f=new Apple();
  21. }
  22. if("Orange".equals(fruitName)){
  23. f=new Orange();
  24. }
  25. return f;
  26. }
  27. }
  28. class hello{
  29. public static void main(String[] a){
  30. fruit f=Factory.getInstance("Orange");
  31. f.eat();
  32. }
  33. }

上面写法的缺点是当我们再添加一个子类的时候,就需要修改工厂类了。如果我们添加太多的子类的时候,改动就会很多。下面用反射机制实现工厂模式:

  1. interface fruit{
  2. public abstract void eat();
  3. }
  4. class Apple implements fruit{
  5. public void eat(){
  6. System.out.println("Apple");
  7. }
  8. }
  9. class Orange implements fruit{
  10. public void eat(){
  11. System.out.println("Orange");
  12. }
  13. }
  14. class Factory{
  15. public static fruit getInstance(String ClassName){
  16. fruit f=null;
  17. try{
  18. f=(fruit)Class.forName(ClassName).newInstance();
  19. }catch (Exception e) {
  20. e.printStackTrace();
  21. }
  22. return f;
  23. }
  24. }
  25. class hello{
  26. public static void main(String[] a){
  27. fruit f=Factory.getInstance("Reflect.Apple");
  28. if(f!=null){
  29. f.eat();
  30. }
  31. }
  32. }

 现在就算我们添加任意多个子类的时候,工厂类都不需要修改。使用反射机制实现的工厂模式可以通过反射取得接口的实例,但是需要传入完整的包和类名。而且用户也无法知道一个接口有多少个可以使用的子类,所以我们通过属性文件的形式配置所需要的子类。

下面编写使用反射机制并结合属性文件的工厂模式(即IoC)。首先创建一个fruit.properties的资源文件:

  1. apple=Reflect.Apple
  2. orange=Reflect.Orange

然后编写主类代码:

  1. interface fruit{
  2. public abstract void eat();
  3. }
  4. class Apple implements fruit{
  5. public void eat(){
  6. System.out.println("Apple");
  7. }
  8. }
  9. class Orange implements fruit{
  10. public void eat(){
  11. System.out.println("Orange");
  12. }
  13. }
  14. //操作属性文件类
  15. class init{
  16. public static Properties getPro() throws FileNotFoundException, IOException{
  17. Properties pro=new Properties();
  18. File f=new File("fruit.properties");
  19. if(f.exists()){
  20. pro.load(new FileInputStream(f));
  21. }else{
  22. pro.setProperty("apple", "Reflect.Apple");
  23. pro.setProperty("orange", "Reflect.Orange");
  24. pro.store(new FileOutputStream(f), "FRUIT CLASS");
  25. }
  26. return pro;
  27. }
  28. }
  29. class Factory{
  30. public static fruit getInstance(String ClassName){
  31. fruit f=null;
  32. try{
  33. f=(fruit)Class.forName(ClassName).newInstance();
  34. }catch (Exception e) {
  35. e.printStackTrace();
  36. }
  37. return f;
  38. }
  39. }
  40. class hello{
  41. public static void main(String[] a) throws FileNotFoundException, IOException{
  42. Properties pro=init.getPro();
  43. fruit f=Factory.getInstance(pro.getProperty("apple"));
  44. if(f!=null){
  45. f.eat();
  46. }
  47. }
  48. }

IOC容器的技术剖析

IOC中最基本的技术就是“反射(Reflection)”编程,通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象,这种编程方式可以让对象在生成时才被决定到底是哪一种对象。只是在Spring中要生产的对象都在配置文件中给出定义,目的就是提高灵活性和可维护性。

我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言提供的反射机制,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。

spring运行原理

main函数如下:

  1. public static void main(String[] args) {
  2. ApplicationContext context = new FileSystemXmlApplicationContext("applicationContext.xml");
  3. Animal animal = (Animal) context.getBean("animal");
  4. animal.say();
  5. }

下面是applicationContext.xml:

  1. <bean id="animal" class="phz.springframework.test.Cat">
  2. <property name="name" value="kitty" />
  3. </bean>

它其中定义了一个bean:phz.springframework.test.Cat

  1. public class Cat implements Animal {
  2. private String name;
  3. public void say() {
  4. System.out.println("I am " + name + "!");
  5. }
  6. public void setName(String name) {
  7. this.name = name;
  8. }
  9. }

Cat类实现了phz.springframework.test.Animal接口

  1. public interface Animal {
  2. public void say();
  3. }

很明显上面的代码输出I am kitty!那么到底Spring是如何做到的呢?接下来写个简单的Spring 来看看Spring 到底是怎么运行的。
首先,我们定义一个Bean类,这个类用来存放一个Bean拥有的属性:

  1. /* Bean Id */
  2. private String id;
  3. /* Bean Class */
  4. private String type;
  5. /* Bean Property */
  6. private Map<String, Object> properties = new HashMap<String, Object>();

一个Bean包括id,type,和Properties。接下来Spring 就开始加载我们的配置文件了,将配置的信息保存在一个HashMap中,HashMap的key就是Bean 的 Id ,HasMap 的value是这个Bean,只有这样才能通过context.getBean("animal")这个方法获得Animal这个类。我们都知道Spirng可以注入基本类型,而且可以注入像List,Map这样的类型,接下来就让我们以Map为例看看Spring是怎么保存的吧 。

Map配置可以像下面的

  1. <bean id="test" class="Test">
  2. <property name="testMap">
  3. <map>
  4. <entry key="a">
  5. <value>1</value>
  6. </entry>
  7. <entry key="b">
  8. <value>2</value>
  9. </entry>
  10. </map>
  11. </property>
  12. </bean>

Spring是怎样保存上面的配置呢?代码如下:

  1. if (beanProperty.element("map") != null) {
  2. Map<String, Object> propertiesMap = new HashMap<String, Object>();
  3. Element propertiesListMap = (Element) beanProperty.elements().get(0);
  4. Iterator<?> propertiesIterator = propertiesListMap.elements().iterator();
  5. while (propertiesIterator.hasNext()) {
  6. Element vet = (Element) propertiesIterator.next();
  7. if (vet.getName().equals("entry")) {
  8. String key = vet.attributeValue("key");
  9. Iterator<?> valuesIterator = vet.elements().iterator();
  10. while (valuesIterator.hasNext()) {
  11. Element value = (Element) valuesIterator.next();
  12. if (value.getName().equals("value")) {
  13. propertiesMap.put(key, value.getText());
  14. }
  15. if (value.getName().equals("ref")) {
  16. propertiesMap.put(key, new String[] { value.attributeValue("bean") });
  17. }
  18. }
  19. }
  20. }
  21. bean.getProperties().put(name, propertiesMap);
  22. }

接下来就进入最核心部分了,让我们看看Spring 到底是怎么依赖注入的吧,其实依赖注入的思想也很简单,它是通过反射机制实现的,在实例化一个类时,它通过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中。让我们看看具体它是怎么做的吧。
首先实例化一个类,像这样

  1. public static Object newInstance(String className) {
  2. Class<?> cls = null;
  3. Object obj = null;
  4. try {
  5. cls = Class.forName(className);
  6. obj = cls.newInstance();
  7. } catch (ClassNotFoundException e) {
  8. throw new RuntimeException(e);
  9. } catch (InstantiationException e) {
  10. throw new RuntimeException(e);
  11. } catch (IllegalAccessException e) {
  12. throw new RuntimeException(e);
  13. }
  14. return obj;
  15. }

接着它将这个类的依赖注入进去,像这样

  1. public static void setProperty(Object obj, String name, String value) {
  2. Class<? extends Object> clazz = obj.getClass();
  3. try {
  4. String methodName = returnSetMthodName(name);
  5. Method[] ms = clazz.getMethods();
  6. for (Method m : ms) {
  7. if (m.getName().equals(methodName)) {
  8. if (m.getParameterTypes().length == 1) {
  9. Class<?> clazzParameterType = m.getParameterTypes()[0];
  10. setFieldValue(clazzParameterType.getName(), value, m,obj);
  11. break;
  12. }
  13. }
  14. }
  15. } catch (SecurityException e) {
  16. throw new RuntimeException(e);
  17. } catch (IllegalArgumentException e) {
  18. throw new RuntimeException(e);
  19. } catch (IllegalAccessException e) {
  20. throw new RuntimeException(e);
  21. } catch (InvocationTargetException e) {
  22. throw new RuntimeException(e);
  23. }
  24. }

最后它将这个类的实例返回给我们,我们就可以用了。我们还是以Map为例看看它是怎么做的,我写的代码里面是创建一个HashMap并把该HashMap注入到需要注入的类中,像这样,

  1. if (value instanceof Map) {
  2. Iterator<?> entryIterator = ((Map<?, ?>) value).entrySet().iterator();
  3. Map<String, Object> map = new HashMap<String, Object>();
  4. while (entryIterator.hasNext()) {
  5. Entry<?, ?> entryMap = (Entry<?, ?>) entryIterator.next();
  6. if (entryMap.getValue() instanceof String[]) {
  7. map.put((String) entryMap.getKey(),
  8. getBean(((String[]) entryMap.getValue())[0]));
  9. }
  10. }
  11. BeanProcesser.setProperty(obj, property, map);
  12. }

IOC配置(配置bean)

什么是bean。首先,spring一个很重要的贡献就是解耦,它将 类(class) 从源文件中抽离出来,放到xml文件中,使得我们可以用xml文件对源文件中的类进行配置和修改,这样就不用每次跑去乱找源文件。但如果我们需要在xml文件中对源文件进行修改,必须在xml文件中建立一种映射关系,也就是说在xml文件中为源文件中的类起一个“别名”,并形成关联,这样我们修改xml文件的时候才会调用源文件的中的对应类和属性。而bean就是源文件中的类在xml文件中的“别名”。

bean的配置形式有两种:通过xml的方式通过注解的方式。其中,通过xml的方式又可以分为三种

  • 通过全类名(反射)方式
  • 通过工厂方法(静态工厂方法&实例工厂方法)
  • FactoryBean的方式。

xml方式

通过全类名(反射)

要求,源文件中要有一个无参的构造函数。

  1. <bean id = helloWorld class = com.cn.HelloWorld”>
  2. <property name=”haha value=”spring”></property>
  3. </bean>

通过工厂方法配置bean

静态工厂方法

直接调用某一个类的静态方法就可以返回bean的实例,不需要创建对象。

  1. package com.yl.factory;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. /**
  5. * 静态工厂方法:直接调用某一个类的静态方法就可与返回bean的实例
  6. * @author yul
  7. *
  8. */
  9. public class StaticCarFactory {
  10. private static Map<String, Car> cars = new HashMap<String, Car>();
  11. static {
  12. cars.put("audi", new Car("audi", 300000));
  13. cars.put("ford", new Car("ford", 300000));
  14. }
  15. /**
  16. * 静态工厂方法
  17. * @param name
  18. * @return
  19. */
  20. public static Car getCar(String name) {
  21. return cars.get(name);
  22. }
  23. }
  1. <!-- 通过静态工厂方法来配置bean,注意不是配置静态工厂方法实例,而是配置bean实例 -->
  2. <!--
  3. class属性:指向静态工厂方法的全类名
  4. factory-method:指向静态工厂方法的名字
  5. constructor-arg:如果静态工厂方法需要传入参数,则使用constructor-arg来配置参数
  6. -->
  7. <bean id="car1"
  8. class="com.yl.factory.StaticCarFactory" factory-method="getCar">
  9. <constructor-arg value="audi"></constructor-arg>
  10. </bean>

实例工厂方法

  1. package com.yl.factory;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. /***
  5. * 实例工厂方法:实例工厂的方法,即现需要创建工厂本身,在调用工厂的实例方法来返回bean的实例
  6. * @author yul
  7. *
  8. */
  9. public class InstanceCarFactory {
  10. private Map<String, Car> cars = new HashMap<String, Car>();
  11. public InstanceCarFactory() {
  12. cars = new HashMap<String, Car>();
  13. cars.put("audi", new Car("audi", 300000));
  14. cars.put("ford", new Car("ford", 400000));
  15. }
  16. public Car getCar(String brand) {
  17. return cars.get(brand);
  18. }
  19. }
  1. <!-- 配置工厂的实例 -->
  2. <bean id="carFactory" class="com.yl.factory.InstanceCarFactory"></bean>
  3. <!--
  4. factory-bean:指向实例工厂方法的bean
  5. factory-method:指向实例工厂方法的名字
  6. constructor-arg:如果实例工厂方法需要传入参数,则使用constructor-arg来配置参数
  7. -->
  8. <!-- 通过实例工厂方法来配置bean -->
  9. <bean id="car2" factory-bean="carFactory" factory-method="getCar">
  10. <constructor-arg value="ford"></constructor-arg>
  11. </bean>

通过FactoryBean配置bean(需要继承FactoryBean接口)

当配置的bean里需要引用其他的bean,通过FactoryBean配置是最好的方式。
过程:定义类实现FactoryBean接口,并实现三个方法:

  • getObject():返回对象
  • getObjectType():返回对象的类型
  • isSingleton():对象是否为单例。

编写配置文件,class指向FactoryBean的全类名。

  1. package com.yl.factorybean;
  2. import org.springframework.beans.factory.FactoryBean;
  3. //自定义的Factorybean需要实现FactoryBean接口
  4. public class CarFactoryBean implements FactoryBean<Car> {
  5. private String brand;
  6. public void setBrand(String brand) {
  7. this.brand = brand;
  8. }
  9. /**
  10. * 返回bean的对象
  11. */
  12. @Override
  13. public Car getObject() throws Exception {
  14. // TODO Auto-generated method stub
  15. return new Car("BMW", 600000);
  16. }
  17. /**
  18. * 返回bean的类型
  19. */
  20. @Override
  21. public Class<?> getObjectType() {
  22. // TODO Auto-generated method stub
  23. return Car.class;
  24. }
  25. @Override
  26. public boolean isSingleton() {
  27. // TODO Auto-generated method stub
  28. return true;
  29. }
  30. }
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  5. <!--
  6. 通过Factorybean来配置bean的实例
  7. class:指向Factorybean的全类名
  8. property:配置Factorybean的属性
  9. 但实际返回的实例却是Factorybean的getObject()方法返回的实例
  10. -->
  11. <bean id="car" class="com.yl.factorybean.CarFactoryBean">
  12. <property name="brand" value="BMW"></property>
  13. </bean>
  14. </beans>

注解方式

组件扫描

spring能够从classpath下自动扫描,侦测和实例化具有特定注解的组件。特定的组件包括:

  • @Component:基本组件,标识了一个受spring管理的组件
  • @Repository:标识持久层组件
  • @Service:标识服务层(业务层)组件
  • @Controller:标识表现层组件

对于扫描的的组件,spring有默认的命名策略:使用非限定类名,第一个字母小写。也可以在注解中通过value属性标识组件的名称。

比如,某个类名为UserService,那么默认的组件名为userService。如果类名为UserServiceImp,则可以用value指明其组件名为userService。也可以用userServiceImp,但是不符合习惯。

当在组件类上使用了特定的注解之后,还需要在spring的配置文件中声明来标识需要扫描的包。可以拥有若干个(需要use-default-filters="false"配合使用)或子节点过滤器,前者表示要包含的目标类,后者表示要排除在外的目标类。

spring内建支持如下四种过滤器:

  • annotation:该过滤器要指定一个annotation名,如lee.AnnotationTest。
  • assignable:类名过滤器,该过滤器直接指定一个java类。
  • regex:正则表达式过滤器,该过滤器指定一个正则表达式,匹配该正则表达式的java类将满足该过滤规则,如org.example.default.*。
  • aspectj:如org.example..*service+。

当需要扫描多个包时,可以使用逗号隔开。

  1. <!--指定扫描的包-->
  2. <context:component-scan
  3. base-package="com.atguigu.spring.beans,annotation"
  4. use-default-filters="false"><!--不再使用默认的filter,只使用指定的filter-->
  5. <!--resource-pattern="repository/*.class">仅扫描repository下的包-->
  6. <!--
  7. <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
  8. -->
  9. <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
  10. </context:component-scan>

其他注解

除了上述四种特定注解,还有以下常用注解:

@Bean

@Bean是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里。使用@Bean注解和使用xml配置bean效果一样。

@Autowired

@Autowired注解可用于修饰属性、setter 方法、构造方法。其作用是为了消除Java代码里面的getter/setter与bean属性中的property。@Autowired注解的意思就是,当Spring发现@Autowired注解时,将自动在代码上下文中找到和其匹配(默认是类型匹配)的Bean,并自动注入到相应的地方去。

@Autowired注解自动装配具有兼容类型的单个bean属性。默认情况下,所有使用@Autowired注解的属性都需要设置,当Spring找不到匹配的bean装配属性时,会抛出异常。若某一属性允许不被设置,可以设置@Autowired注解的required属性为false。

默认情况下,当IOC容器里存在多个类型兼容的bean时(例如现在UserDao接口有两个实现类,UserDaoImpl只是其一),通过类型的自动装配将无法工作。有两种解决方式,一种是通过@Repository(“userDao”)设置UserDaoImpl实例的名称为userDao,然后在UserService中声明UserDao组件名为userDao即可。第二种方式是在装配属性上使用@Qualifier注解进行设置。

  1. public class Zoo {
  2. //getter和setter可以去掉
  3. @Autowired
  4. private Tiger tiger;
  5. @Autowired
  6. private Monkey monkey;
  7. public String toString(){
  8. return tiger + "\n" + monkey;
  9. }
  10. }
  1. <beans
  2. <context:component-scan base-package="com.spring" />
  3. <!--属性注入可以去掉-->
  4. <bean id="zoo" class="com.spring.model.Zoo" />
  5. <bean id="tiger" class="com.spring.model.Tiger" />
  6. <bean id="monkey" class="com.spring.model.Monkey" />
  7. </beans>

@Required

@Required 注解只能用于修饰 bean 属性的 setter 方法。受影响的 bean 属性必须在配置时被填充在 xml 配置文件中,否则容器将抛出BeanInitializationException。

例如,下述程序会出错,因为age的setter方法没有在bean中注入,而age的setter方法标记了@Required,也就是必须要输入,抛出的异常为:BeanInitializationException。

  1. package com.jsoft.testspring.testannotationrequired;
  2. import org.springframework.beans.factory.annotation.Required;
  3. public class Student {
  4. private Integer age;
  5. private String name;
  6. @Required
  7. public void setAge(Integer age){
  8. this.age = age;
  9. }
  10. public Integer getAge(){
  11. return this.age;
  12. }
  13. public void setName(String name){
  14. this.name = name;
  15. }
  16. public String getName(){
  17. return this.name;
  18. }
  19. }
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context.xsd">
  9. <context:annotation-config/>
  10. <bean id="student" class="com.jsoft.testspring.testannotationrequired.Student">
  11. <property name="name" value="Jim"/>
  12. <!--这样补充之后,就不会报错了。
  13. <property name="age" value="27"/>
  14. -->
  15. </bean>
  16. </beans>

@Qualifier

在@Autowired注解中,提到了如果发现有多个候选的 bean 都符合修饰类型,Spring 就会抓瞎了。

那么,如何解决这个问题。

可以通过@Qualifier指定 bean 名称来锁定真正需要的那个 bean。

  1. public class AnnotationQualifier {
  2. private static final Logger log = LoggerFactory.getLogger(AnnotationQualifier.class);
  3. @Autowired
  4. @Qualifier("dog") /** 去除这行,会报异常 */
  5. Animal dog;
  6. Animal cat;
  7. public Animal getDog() {
  8. return dog;
  9. }
  10. public void setDog(Animal dog) {
  11. this.dog = dog;
  12. }
  13. public Animal getCat() {
  14. return cat;
  15. }
  16. @Autowired
  17. public void setCat(@Qualifier("cat") Animal cat) {
  18. this.cat = cat;
  19. }
  20. public static void main(String[] args) throws Exception {
  21. AbstractApplicationContext ctx =
  22. new ClassPathXmlApplicationContext("spring/spring-annotation.xml");
  23. AnnotationQualifier annotationQualifier =
  24. (AnnotationQualifier) ctx.getBean("annotationQualifier");
  25. log.debug("Dog name: {}", annotationQualifier.getDog().getName());
  26. log.debug("Cat name: {}", annotationQualifier.getCat().getName());
  27. ctx.close();
  28. }
  29. }
  30. abstract class Animal {
  31. public String getName() {
  32. return null;
  33. }
  34. }
  35. class Dog extends Animal {
  36. public String getName() {
  37. return "狗";
  38. }
  39. }
  40. class Cat extends Animal {
  41. public String getName() {
  42. return "猫";
  43. }
  44. }
  1. <!-- 测试@Qualifier -->
  2. <bean id="dog" class="org.zp.notes.spring.beans.annotation.sample.Dog"/>
  3. <bean id="cat" class="org.zp.notes.spring.beans.annotation.sample.Cat"/>
  4. <bean id="annotationQualifier" class="org.zp.notes.spring.beans.annotation.sample.AnnotationQualifier"/>

JSR(Java Specification Requests,Java规范提案)注解

@Resource

@Resource注解与@Autowired注解作用非常相似

  1. public class AnnotationResource {
  2. private static final Logger log = LoggerFactory.getLogger(AnnotationResource.class);
  3. @Resource(name = "flower")
  4. Plant flower;
  5. @Resource(name = "tree")
  6. Plant tree;
  7. public Plant getFlower() {
  8. return flower;
  9. }
  10. public void setFlower(Plant flower) {
  11. this.flower = flower;
  12. }
  13. public Plant getTree() {
  14. return tree;
  15. }
  16. public void setTree(Plant tree) {
  17. this.tree = tree;
  18. }
  19. public static void main(String[] args) throws Exception {
  20. AbstractApplicationContext ctx =
  21. new ClassPathXmlApplicationContext("spring/spring-annotation.xml");
  22. AnnotationResource annotationResource =
  23. (AnnotationResource) ctx.getBean("annotationResource");
  24. log.debug("type: {}, name: {}", annotationResource.getFlower().getClass(), annotationResource.getFlower().getName());
  25. log.debug("type: {}, name: {}", annotationResource.getTree().getClass(), annotationResource.getTree().getName());
  26. ctx.close();
  27. }
  28. }
  1. <!-- 测试@Resource -->
  2. <bean id="flower" class="org.zp.notes.spring.beans.annotation.sample.Flower"/>
  3. <bean id="tree" class="org.zp.notes.spring.beans.annotation.sample.Tree"/>
  4. <bean id="annotationResource" class="org.zp.notes.spring.beans.annotation.sample.AnnotationResource"/>

@Resource的装配顺序:

  • @Resource后面没有任何内容,默认通过name属性去匹配bean,找不到再按type去匹配
  • 指定了name或者type则根据指定的类型去匹配bean
  • 指定了name和type则根据指定的name和type去匹配bean,任何一个不匹配都将报错

@Autowired和@Resource两个注解的区别:

  • @Autowired默认按照byType方式进行bean匹配,@Resource默认按照byName方式进行bean匹配
  • @Autowired是Spring的注解,@Resource是J2EE的注解,这个看一下导入注解的时候这两个注解的包名就一清二楚了

Spring属于第三方的,J2EE是Java自己的东西,因此,建议使用@Resource注解,以减少代码和Spring之间的耦合。

参考

注解方式和xml文件方式的关系



DI(依赖注入方式)

依赖注入也可以叫做属性注入,主要有三种方式:setter方法注入、构造器注入和工厂方法注入(不常用)。

setter注入

使用这种方法时,bean要有一个setter方法,在下例中,类中必须有一个setName()方法。

  1. <bean id = "helloWorld" class="com.cn.HelloWorld">
  2. <property name="name" value="World"></property>
  3. </bean>

构造器注入

  1. <bean id = "car" class = "com.cn.Car">
  2. <constructor-arg vaule="audi" index="0" />
  3. <constructor-arg vaule="shangahi" index="1" />
  4. <constructor-arg vaule="3000" type="double" />
  5. <constructor-arg vaule="audi" type="java.lang.String" />
  6. <constructor-arg vaule="shangahi" type="java.long.String" />
  7. <constructor-arg vaule="3000" type="int" />
  8. </bean>

其中,index表示参数的顺序,type表示参数的类型,可以混用。type可以用来解决构造器注入歧义的问题。

工厂方法注入(比较少用,先放置)

setter注入的三种写法

setter注入又有是那种不同的写法:

正常方式

  1. <bean id="FileNameGenerator" class="com.yiibai.common.FileNameGenerator">
  2. <property name="name">
  3. <value>yiibai</value>
  4. </property>
  5. <property name="type">
  6. <value>txt</value>
  7. </property>
  8. </bean>

注:使用构造器注入的时候也可以按照这种方法写。比如上述构造器注入的最后一个属性可以这样写:

  1. <constructor-arg type="int" >
  2. <value>3000</value>
  3. <constructor-arg />

注:若value中的值有特殊字符,比如<>,可以用进行转义,例如;

  1. <constructor-arg type="java.long.String" />
  2. <value><![CDATA[<Shanghai>]]></value>
  3. <constructor-arg />

快捷方式

  1. <bean id="FileNameGenerator" class="com.yiibai.common.FileNameGenerator">
  2. <property name="name" value="yiibai" />
  3. <property name="type" value="txt" />
  4. </bean>

p模式

  1. <bean id="FileNameGenerator" class="com.yiibai.common.FileNameGenerator"
  2. p:name="yiibai" p:type="txt" />

在一个bean里引用另一个bean

ref=”beanName”

setter

  1. <bean id=”person” class=”com.cn.Person”>
  2. <property name=”car” ref=”Car”>
  3. </bean>

构造器

  1. <bean id = “person” class = “com.cn.Personr”>
  2. <constructor-arg index=”0” ref=”Car” />
  3. </bean>

ref bean=”beanName”

setter

  1. <bean id=”person” class=”com.cn.Person”>
  2. <property name=”car” >
  3. <ref bean=”Car”/>
  4. </property>
  5. </bean>

构造器

  1. <bean id = “person” class = “com.cn.Personr”>
  2. <constructor-arg index=”0” />
  3. <ref bean=”Car”/>
  4. <constructor-arg/>
  5. </bean>

内部bean

  1. <bean id=”person” class=”com.cn.Person”>
  2. <property name=”name” value=”Tom”></property>
  3. <property name=”age” value=”30”></property>
  4. <property name=”car”>
  5. <bean class=”com.cn.Car”>
  6. <constructor-arg value=”Ford”></constructor-arg>
  7. <constructor-arg value=”Changan”></constructor-arg>
  8. <constructor-arg value=”20000” type=”double”></constructor-arg>
  9. </bean>
  10. </property>
  11. </bean>

注:内部bean不能被外部引用;内部bean不用写id。

级联属性赋值

属性首先需要初始化才可以为级联属性赋值。

  1. <!--初始化属性-->
  2. <property name=”car”>
  3. <bean……
  4. </property>
  5. <!--为car中的属性赋值-->
  6. <property name=”car.maxSpeed” value=”260”></properry>

集合属性

List示例

  1. <property name="lists">
  2. <list>
  3. <value>1</value>
  4. <ref bean="PersonBean" />
  5. <bean class="com.yiibai.common.Person">
  6. <property name="name" value="yiibaiList" />
  7. <property name="address" value="Hainan" />
  8. <property name="age" value="28" />
  9. </bean>
  10. </list>
  11. </property>

Set示例

  1. <property name="sets">
  2. <set>
  3. <value>1</value>
  4. <ref bean="PersonBean" />
  5. <bean class="com.yiibai.common.Person">
  6. <property name="name" value="yiibaiSet" />
  7. <property name="address" value="Hainan" />
  8. <property name="age" value="28" />
  9. </bean>
  10. </set>
  11. </property>

Map示例

  1. <property name="maps">
  2. <map>
  3. <entry key="Key 1" value="1" />
  4. <entry key="Key 2" value-ref="PersonBean" />
  5. <entry key="Key 3">
  6. <bean class="com.yiibai.common.Person">
  7. <property name="name" value="yiibaiMap" />
  8. <property name="address" value="Hainan" />
  9. <property name="age" value="28" />
  10. </bean>
  11. </entry>
  12. </map>
  13. </property>

Properties示例

  1. <property name="pros">
  2. <props>
  3. <prop key="admin">admin@yiibai.com</prop>
  4. <prop key="support">support@yiibai.com</prop>
  5. </props>
  6. </property>

单例的集合bean

配置单例的集合bean,以供多个bean进行调用。

  1. <util:list id="cars">
  2. <ref bean="car"/>
  3. <ref bean="car2"/>
  4. </util:list>
  5. <!--调用上面配置的bean-->
  6. <bean id=”person” class=”com.cn.Person”>
  7. <property name=”name” value=”Jack”></property>
  8. <property name=”age” value=”29”></property>
  9. <property name=”cars” ref=”cars”></property>
  10. </bean>

参考

spring、pring boot和spring mvc的区别

spring boot就是一个大框架里面包含了许许多多的东西,其中spring就是最核心的内容之一,当然就包含spring mvc。

spring mvc 是只是spring 处理web层请求的一个模块。

因此他们的关系大概就是这样:

spring mvc < spring

spring boot 我理解就是把 spring spring mvc spring data jpa 等等的一些常用的常用的基础框架组合起来,提供默认的配置,然后提供可插拔的设计,就是各种 starter ,来方便开发者使用这一系列的技术,套用官方的一句话, spring 家族发展到今天,已经很庞大了,作为一个开发者,如果想要使用 spring 家族一系列的技术,需要一个一个的搞配置,然后还有个版本兼容性问题,其实挺麻烦的,偶尔也会有小坑出现,其实蛮影响开发进度, spring boot 就是来解决这个问题,提供了一个解决方案吧,可以先不关心如何配置,可以快速的启动开发,进行业务逻辑编写,各种需要的技术,加入 starter 就配置好了,直接使用,可以说追求开箱即用的效果吧.

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注