zoukankan      html  css  js  c++  java
  • Spring框架的初步学习

    (1) IOC 控制反转
    所谓的控制反转就是应用本身不负责依赖对象的创建和维护,依赖对象的创建及维护是由
    外部容器负责的(spring是外部容器之一)。这样控制权就由应用转移到了外部容器,控制权
    的转移就是所谓的反转。
    实例代码:
        //普通
        public class PersonServiceBean {
            private PersonDao personDao = new PersonDaoBean();
     
            public void save(Person person) {
                personDao.save(person);
            }
        }
        //IOC方式
       public class PersonServiceBean {
            private PersonDao personDao;
            //通过构造器参数,让容器把创建好的依赖对象注入进PersonServiceBean,当然也可以用setter的方式进行注入
            public PersonServiceBean(PersonDao personDao) {
                this.personDao = personDao;
            }
            
            public void save(Person person) {
                personDao.save(person);
            }
        }
    所谓依赖注入(Dependency Injection)就是指:在运行期间,由外部容器动态地将依赖对象注入到组件中。
    (2)Spring的好处
    • 降低组件之间的耦合度;
    • 提供了很多服务,事务管理服务,spring core核心服务,持久化服务等等。事务管理方面,不需要我们手工控制事务,也需要手动的去处理事务的传播行为;
    • 容器提供单例模式支持,不需要再编写实现代码;
    • AOP技术,很容易实现权限拦截,运行期监控等功能;
    • 提供了很多的辅助类,如JdbcTemplate,HibernateTemplate
    • 提供了主流应用框架集成的支持,如hibernate,mybatis
    (3)轻量级和重量级
    主要看它使用了多少服务,使用的服务越多,容器要为普通java对象做的工作越多,必然会影响到应用的发布时间或者是运行性能;
    (4)入门知识点
        实例化Spring容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"bean.xml"});
        Spring的配置文件可以指定多个,通过数组传入多个配置文件
    spring是如何实例化bean的:
    1. 解析配置的xml文件,获取到beans节点下面的bean
    2. 将bean装入集合,对外提供getBean方法,通过反射技术,Class.forName('xxx').newInstance()方法来获取到bean对象的实例
    /**
         * 读取xml配置文件
         * @param filename
         */
        private void readXML(String filename) {
               SAXReader saxReader = new SAXReader();   
                Document document=null;   
                try{
                 URL xmlpath = this.getClass().getClassLoader().getResource(filename);
                 document = saxReader.read(xmlpath);
                 Map<String,String> nsMap = new HashMap<String,String>();
                 nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空间
                 XPath xsub = document.createXPath("//ns:beans/ns:bean");//创建beans/bean查询路径
                 xsub.setNamespaceURIs(nsMap);//设置命名空间
                 List<Element> beans = xsub.selectNodes(document);//获取文档下所有bean节点 
                 for(Element element: beans){
                    String id = element.attributeValue("id");//获取id属性值
                    String clazz = element.attributeValue("class"); //获取class属性值 
                    BeanDefinition beanDefine = new BeanDefinition(id, clazz);
                    XPath propertySub = element.createXPath("ns:property");
                    propertySub.setNamespaceURIs(nsMap);
                    List<Element> propertiesElement = propertySub.selectNodes(element);
                    for (Element property : propertiesElement) {
                        String name = property.attributeValue("name");
                        String ref = property.attributeValue("ref");
                        String value = property.attributeValue("value");
    //                    System.out.println(name + "==" + ref);
                        beanDefine.getProperties().add(new PropertyDefinition(name, ref,value));
                    }
                    beanDefines.add(beanDefine);
                 }   
                }catch(Exception e){   
                    e.printStackTrace();
                }
        }
    View Code
    /**
         * 完成bean的实例化
         */
        private void instanceBeans() {
            for(BeanDefinition beanDefinition : beanDefines){
                try {
                    if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))
                        sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            
        }
    View Code
    是如何依赖注入bean的:
         3.   在每个bean对应的java对象中,有需要注入的bean的set方法,通过Introspector.getBeanInfo获取到被注入bean对象的属性值,与配置文件中配置的bean进行匹配,如果匹配上通过pDescriptor.getWriteMethod方法,反射的方式将匹配到的bean注入到属性中去。
    /**
         * 为bean对象的属性注入值
         */
        private void injectObject() {
            for(BeanDefinition beanDefinition : beanDefines) {
                Object bean = sigletons.get(beanDefinition.getId());
                try {
                    if(null != bean) {
                        //得到bean的属性集合(<bean id="personDao" class="com.test.dao.impl.PersonDaoImpl" />)
                        PropertyDescriptor[] pd = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();//包含class属性
                        for(PropertyDefinition propertyDefinition : beanDefinition.getProperties()) {//配置文件中用户定义的bean属性
                            for(PropertyDescriptor pDescriptor : pd) {
                                if(propertyDefinition.getName().equals(pDescriptor.getName())) {
                                    Method setter = pDescriptor.getWriteMethod();//获取属性的setter方法
                                    if(null != setter) {
                                        Object value = null;
                                        if(propertyDefinition.getRef() != null && !propertyDefinition.getRef().trim().equals("")) {
                                            value = sigletons.get(propertyDefinition.getRef());
                                        }else {
                                            value = ConvertUtils.convert(propertyDefinition.getValues(), pDescriptor.getPropertyType());//注入基本类型的值
                                        }
                                        setter.setAccessible(true);//如果setter方法是private的话,invoke会报错,需要设置
                                        setter.invoke(bean, value);//把引用对象注入到属性
                                    }
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    View Code
    实例化bean的方式有三种:
    1.  构造函数(用的最多)
    2. 静态工厂
    3. 实例工厂
    bean的作用域:
    • singleton(默认):在spring ioc容器中一个bean定义只有一个实例对象,默认情况下会在容器启动的时候初始化,如果需要延迟初始化,需要在配置bean的时候加上lazy_init="true",这样只有在获取的时候才会初始化,对单个bean <bean id="person" class="com.test.bean.Person" lazy-init="true">,容器中所有bean都延迟加载<beans default-lazy-init="true">
    • prototype:每次从容器获取新的bean对象 <bean id="person" class="com.test.bean.Person" scope="prototype">,在getBean的时候进行实例化
    bean的生命周期:
        默认情况下容器启动,AbstractApplicationContext中给出了一个close方法,
        其说明是Close this application context, destroying all beans
        AbstractApplicationContext ctx =  new ClassPathXmlApplicationContext(new String[]{"bean.xml"});
        ctx.close();
    依赖注入手工装配或自动装配,自动装配会产生未知情况,不利于开发)
    •  set属性的方式注入bean:(可以被多个bean同时使用)
          <bean id="personService" class="com.test.service.impl.PersonServiceImpl">
              <property name="personDao" ref="personDao"></property> 
                    <!-- name是指要注入的属性,ref对应要注入的bean的名称,通过反射技术将bean设给了属性 -->
             </bean>
    • 内部bean的方式注入(只可以被这一个bean使用)
    • 使用构造器注入
              通过 constructor-arg 来注入
    • 使用Field注入(用于注解方式)
      1. 引入jar文件 common.annotations.jar
      2. 在xml添加配置如下
           命名空间  xmlns:context="http://www.springframework.org/schema/context"
           schema
              http://www.springframework.org/schema/context


    • 打开注解 <context:annotation-config />,这个配置隐式注册了多个对注释进行解析处理的处理器
    • 使用@Autowired或@Resource注解方式进行装配。区别在于:@Autowired默认按类型装配,
            @Resource默认按名称装配。建议使用Resource,这个是jdk提供的注解。
            自定义一个注解,该注解可用于set方法和字段上面,供处理器处理,将bean注入到字段或set方法中
    package junit.test;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD,ElementType.METHOD})
    public @interface TydicResource {
        /**
         * Retention(保留)注解说明,这种类型的注解会被保留到那个阶段. 有三个值: 
         * 1.RetentionPolicy.SOURCE —— 这种类型的Annotations只在源代码级别保留,编译时就会被忽略 
         * 2.RetentionPolicy.CLASS —— 这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略
         * 3.RetentionPolicy.RUNTIME —— 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用.
         * 
         * Target(注解的作用目标)
         */
        String name() default "";
    
    }
    View Code
    package com.test.service.impl;
    
    import javax.annotation.Resource;
    
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    import org.springframework.stereotype.Service;
    
    import junit.test.TydicResource;
    
    import com.test.dao.PersonDao;
    import com.test.service.PersonService;
    
    @Service("personService") @Scope("prototype")
    public class PersonServiceImpl extends Object implements PersonService {
    
    //    @TydicResource(name="xgw")<!-- 注解通过name寻找到bean注入 -->
        @TydicResource
    //    @Resource
        public PersonDao personDao;
        
        public PersonDao getPersonDao() {
            return personDao;
        }
        public void setPersonDao(PersonDao personDao) {
            this.personDao = personDao;
        }
        @Override
        public void add() {
            personDao.add();
        }
    }
    View Code

    查看set方法和field上是否加了注解

    /**
         * 通过注解方式注入bean
         * 仿 注解处理器代码
         */
        private void annotationInjectObject() {
            for(String beanName : sigletons.keySet()) {
                 Object bean = sigletons.get(beanName);
                 if(null != bean) {
                     try {
                        PropertyDescriptor[] pd = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();//定义的javabean的属性集合
                        for(PropertyDescriptor pDescriptor : pd) {
                            Method setter = pDescriptor.getWriteMethod(); //属性的set方法
                            if(null != setter && setter.isAnnotationPresent(TydicResource.class)) {
                                TydicResource resource = setter.getAnnotation(TydicResource.class);//是否存在这个注解
                                Object value = null;
                                if(resource.name()!=null && !"".equals(resource.name())) {//注解中标明name属性
                                    value = sigletons.get(resource.name());
                                }else {
                                    value = sigletons.get(pDescriptor.getName());
                                    if(value == null) {                                    //如果在属性中也没有找到,就按类型去寻找                        
                                        for(String key : sigletons.keySet()) {
                                            /**
                                             * isAssignableFrom
                                             * either the same as, or is a superclass or * superinterface of, the class or interface
                                             * 这个属性的类型如果是该磊的接口或者父类或者就是该类的话,返回true
                                             */
                                            if(pDescriptor.getPropertyType().isAssignableFrom(sigletons.get(key).getClass())) {
                                                value = sigletons.get(key);
                                                break;
                                            }
                                        }
                                    }
                                }
                                setter.setAccessible(true);
                                setter.invoke(bean, value);
                            }
                        }
                        
                        Field[] fields = bean.getClass().getFields();
                        for(Field field : fields) {
                                TydicResource resource = field.getAnnotation(TydicResource.class);//是否存在这个注解
                                Object value = null;
                                if(resource.name()!=null && !"".equals(resource.name())) {//注解中标明name属性
                                    value = sigletons.get(resource.name());
                                }else {
                                    value = sigletons.get(field.getName());
                                    if(value == null) {                                    //如果在属性中也没有找到,就按类型去寻找                        
                                        for(String key : sigletons.keySet()) {
                                            if(field.getType().isAssignableFrom(sigletons.get(key).getClass())) {
                                                value = sigletons.get(key);
                                                break;
                                            }
                                        }
                                    }
                                }
                                field.setAccessible(true);
                                field.set(bean, value);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                 }
            }
        }
    View Code
    自动扫描
    spring2.5以后引入组件自动扫描机智,可以在类路径下寻找标注了
    @Component 泛指组件,可通用
    @Service         用于标注业务层组件
    @Controller    用于标注控制层组件(如action)
    @Respostory   用于标注数据访问组件dao层
    注解的类,纳入到spring容器中管理
    在配置文件中添加 <context:component-scan base-package="com.test.*" />,base-package为需要被扫描的包路径,
    有了 <context:component-scan base-package="com.test.*" />配置之后就可以将<context:annotation-config />去掉,
    前者注册的处理器包含了后者的。
    由于spring初始化的时候采用的是单例,如果需要实现多个bean的话,可以加@Scope("prototype")来改变。
    @PostConstruct   默认初始化方法
    @PreDestroy
    (4)AOP
    使用Proxy创建代理对象的时候,目标对象必须实现一个接口,面向接口的时候才能使用Proxy创建代理对象。
     
    joinpoint(连接点)
        被拦截的方法,需要被处理的可以被看做连接点(joinpoint),在spring中只支持方法类型的连接点,实际上连接点还可以是field或类构造器
    pointcut(切入点)
        是指所有要被拦截的joinpoint定义
    weave(织入):
        指将aspect应用到target对象并导致proxy对象创建的过程叫织入
    Introdunction(引入):
        在不修改代码的情况下,在运行期动态的添加方法或field
    aspect(切面):
        是指对横切性关注点的抽象,就像类是对实体的抽象一样。    
    抽象过程的体现
    package com.test.aop;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    import com.test.service.impl.PersonServiceImpl;
    
    public class JDKProxyFactory implements InvocationHandler {
        private Object targetObject;
    
        public Object createProxyInstance(Object targetObject) {
            this.targetObject = targetObject;
            return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            PersonServiceImpl personServiceImpl = (PersonServiceImpl) this.targetObject;
            Object result = null;
            if(personServiceImpl.getUser()!=null) {
                 result = method.invoke(targetObject, args);
            }
             
            return result;
        }
    }
    View Code
    package com.test.aop;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import com.test.service.impl.PersonServiceImpl;
    
    public class CglibProxyFactory implements MethodInterceptor {
        private Object targetObject;//代理的目标对象
        
        public Object createProxyInstance(Object targetObject) {
            this.targetObject = targetObject;
            Enhancer enhancer = new Enhancer();//该类用于生成代理
            /**
             * cglib创建的代理,是目标对象的子类,能够复制非final修饰的所有方法
             */
            enhancer.setSuperclass(this.targetObject.getClass());//设置父类
            enhancer.setCallback(this);//设置回调用对象本身
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object proxy, Method method, Object[] aobj,
                MethodProxy methodproxy) throws Throwable {
            PersonServiceImpl personServiceImpl = new PersonServiceImpl();
            Object result = null;
            if(personServiceImpl.getUser() != null) {
                result = methodproxy.invoke(targetObject, aobj);
            }
            return result;
        }
    
    }
    View Code
    Spring AOP编程
    如果Spring检测到类实现了接口,则会用jdk提供的Proxy动态代理技术进行拦截,如果类没有实现接口,则会用cglib的方式进行创建代理对象。
    切面:@Aspect
    声明一个切入点:@Pointcut("executor(* com.test.service..*.*(..))")
    各种通知:@Before,@AfterReturning,@After,@AfterThrowing
    @Around     需要加上切入点("anyMethod()"),才可以。
    首先默认会拦截切点后面所有指定的方法
    同时可以再添加限制条件,进行拦截限制
      
    (5)事务管理
    引入命名空间:xmlns:tx="http://www.springframework.org/schema/tx
    //配置数据源
    • 直接配置在spring的配置文件中
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
       <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
       <property name="url" value="jdbc:mysql://localhost:3306/test"></property>
       <property name="username" value="root"></property>
       <property name="password" value="root"></property>
      </bean>
    • 配置在配置文件(db.properties)中
    采用第一种方式即可,配置文件方式了解即可
      //配置事务管理器
      <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource"></property>
      </bean>
    //打开事务管理
    <tx:annotation-driven transaction-manager="txManager" />
     
    运行期(RuntimeException unchecked)异常会进行回滚
    可以通过设置,让RuntimeException异常不回滚
    可以通过设置,让checked的异常回滚
     
    由于事务会影响性能,所以如果不需要进行事务管理的话,就要声明该方法不需要事务管理,比如说查询列表或者查情详情,事务会被挂起,在该方法调用结束后,原先的事务会恢复执行。@Transactional(propagation=Propagation.NOT_SUPPORTED)
     
    XML方式配置事务
    建议使用注解方式,但是在实际项目中运用大多是xml方式。
     
    数据库事务隔离级别
     
    奋斗
  • 相关阅读:
    Linux命令-网络命令:netstat
    Linux命令-网络命令:traceroute
    Linux命令-网络命令:lastlog
    Linux命令-网络命令:last
    mongodb3.4 安装及用户名密码设置
    MySQL表名不区分大小写的设置方法
    数据库设计中的四个范式
    dubbo本地调试直连
    com.mysql.jdbc.PacketTooBigException: Packet for query is too large (1169 > 1024)
    Linux服务器时间同步
  • 原文地址:https://www.cnblogs.com/ClearX/p/5499529.html
Copyright © 2011-2022 走看看