1、Spring企业开发 Spring是什么 Spring是一个开源的控制反转 (Inversion of Control ,IoC)和面向切面 (AOP)的 容器 框架 .它的主要目得是简化企业开发 . IOC 控制反转 public class PersonServiceBean private PersonDao personDao = new PersonDaoBean(); public void save(Person person) personDao.save(person); PersonDaoBean 是在 应用内部 创建及维护的 。所谓控制反转就是 应用本身不负责 依赖对象的
2、创建及维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转。 依赖注入 (Dependency Injection) 当我们把依赖对象交给外部容器负责创建,那么 PersonServiceBean 类可以改成如下: public class PersonServiceBean private PersonDao personDao ; /通过构造器参数,让容器把创建好的依赖对象注入进 PersonServiceBean,当然也可以使用setter方法进行注入。 public PersonServiceBean(PersonDao perso
3、nDao) this.personDao=personDao; public void save(Person person) personDao.save(person); 所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中。 为何要使用 Spring 至少在我看来,在项目中引入 spring立即可以带来下面的好处 降低组件之间的耦合度 ,实现软件各层之间的解耦。 可以使用容器提供的众多服务,如:事务管理服务、消息服务等等。当我们使用容器管理事务时,开发人员就不再需要手工控制事务 .也不需处理复杂的事务传播。 容器提供单例模式支持,开发人员不再需要自己编写实现代码。 容器提
4、供了 AOP技术,利用它很容易实现如权限拦截、运行期监控等功能。 容器提供的众多辅作类,使用这些类能够加快应用的开发,如: JdbcTemplate、 HibernateTemplate。 Spring对于主流的应用框架提供了集成支持,如:集成 Hibernate、 JPA、 Struts等,这样更便于应用的开发。 Controller Service DAO 使用 Spring的好处 当使用 spring时,我们可以使用容器提供的众多服务 如果使用 Spring, 我们就不再需要手工控制事务 Hibernate的事务操作 : public void save() Session sessio
5、n = sessionFactory.getCurrentSession(); session.beginTransaction(); Info info = new Info(“传智播客 “); info.setContent(“国内实力最强的 java培训机构 “); session.save(info ); session.getTransaction().commit(); JDBC的事务操作 : Connection conn = null; try . conn.setAutoCommit(false); Statement stmt = conn.createStatement(
6、); stmt.executeUpdate(“update person where name=叶天 “); mit(); . catch (Exception e) conn.rollback(); finallyconn.close(); 另外,如果使用 spring, 我们也不需要处理复杂的事务传播行为 public void payment() Bean1.update();/更新金额 Bean2.save();/记录操作日志 如果我们不使用 Spring,针对下面这两种业务需求,我们该如何做? 第 1种可能的业务需求:要求 Bean1.update()和 Bean2.save()在同
7、一个事务中执行。 第 2种可能的业务需求:要求不管 Bean1.update() 的事务是否成功,都需要记录操作日志。 public class Bean1 public void update()/注意:下面省略了一些代码 Connection conn = null; conn.setAutoCommit(false); Statement.executeUpdate(“update account set amount=? where id=?“); public class Bean2 public void save()/注意:下面省略了一些代码 Connection conn =
8、null; conn.setAutoCommit(false); Statement.executeUpdate(“insert into Log (content) values (?)“); 使用 Spring,不再需要我们处理复杂的事务传播行为 使用 Spring,我们只需要通过声明式的事务属性配置就可以轻松地实现这两种业务需求 1.要求 Bean1.update()和 Bean2.save()的在同一个事务中执行 2.要求不管 Bean1.update() 的事务是否成功,都需要记录日志。 Transactional(propagation=Propagation.Required)
9、public void payment() Bean1.update();/更新金额 Bean2.save();/记录日志 public class Bean1 Transactional(propagation=Propagation.Required) public void update() executeUpdate(“update account set amount=? where id=?“); public class Bean2 Transactional(propagation=Propagation.RequiresNew) public void save() exec
10、uteUpdate(“insert into Log (content) values (?)“); 轻量级与重量级概念的划分 经常会有同学问到 spring属于轻量级框架 ,还是重量框架 ?其实划分一个应用是否属于轻量级还是重量级 ,主要看它使用了多少服务 .使用的服务越多 ,容器要为普通 java对象做的工作就越多 ,必然会影响到应用的发布时间或者是运行性能 . 对于 spring容器,它提供了很多服务 , 但这些服务并不是默认为应用打开的 , 应用需要某种服务 , 还需要指明使用该服务 , 如果应用使用的服务很少 , 如 :只使用了 spring核心服务 , 那么我们可以认为此时应用属于
11、轻量级的 , 如果应用使用了 spring提供的大部分服务 , 这时应用就属于重量级。目前 EJB容器就因为它默认为应用提供了 EJB规范中所有的功能 , 所以它属于重量级。 使用 Spring需要的 jar 到 http:/www.springsource.org/download下载 spring,然后进行解压缩,在解压目录中找到下面 jar文件,拷贝到类路径下 distspring.jar libjakarta-commonscommons-logging.jar 如果使用了切面编程 (AOP),还需要下列 jar文件 lib/aspectj/aspectjweaver.jar和 asp
12、ectjrt.jar lib/cglib/cglib-nodep-2.1_3.jar 如果使用了 JSR-250中的注解 ,如 Resource/PostConstruct/PreDestroy,还需要下列 jar文件 libj2eecommon-annotations.jar spring的配置文件模版 . 该配置模版可以从 spring的参考手册或 spring的例子中得到。配置文件的取名可以任意,文件可以存放在任何目录下,但考虑到通用性,一般放在类路径下。 编写 spring配置文件时,不能出现帮助信息 由于 spring的 schema文件位于网络上,如果机器不能连接到网络,那么在编写
13、配置信息时候就无法出现提示信息,解决方法有两种: 1。 让机器上网, eclipse会自动从网络上下载 schema文件并缓存在硬盘上。 2。 手动添加 schema文件 ,方法如下: windwos-preferences-myeclipse-files and editors-xml-xmlcatalog 点 “add“,在出现的窗口中的 Key Type中选择 URI,在 location中选 “File system“,然后在 spring解压目录的 dist/resources目录中选择 spring-beans-2.5.xsd,回到设置窗口的时候不要急着关闭窗口 ,应把窗口中的 K
14、ey Type改为 Schema location,Key改为http:/www.springframework.org/schema/beans/spring-beans-2.5.xsd 实例化 spring容器 实例化 Spring容器常用的两种方式: 鉴于最近分手的特别多,本人有个建议:你可以和你的男(女)朋友各自找一个分手的理由,如果相同,恭喜你们,可以分了! 方法一 : 在类路径下寻找配置文件来实例化容器 ApplicationContext ctx = new ClassPathXmlApplicationContext(new String“beans.xml“); 方法二 :
15、在文件系统路径下寻找配置文件来实例化容器 ApplicationContext ctx = new FileSystemXmlApplicationContext(new String“d: beans.xml“); Spring的配置文件可以指定多个,可以通过 String数组传入。 从 spring容器中得到 bean 当 spring容器启动后,因为 spring容器可以管理 bean对象的创建,销毁等生命周期,所以我们只需从容器直接获取 Bean对象就行,而不用编写一句代码来创建 bean对象。从容器获取 bean对象的代码如下: ApplicationContext ctx = ne
16、w ClassPathXmlApplicationContext(“beans.xml”); OrderService service = (OrderService)ctx.getBean(“personService“); 使用 dom4j读取 spring配置文件 public class ItcastClassPathXmlApplicationContext private List beanDefines = new ArrayList(); public ItcastApplicationContext(String filename) init(filename); priva
17、te void init(String filename) SAXReader saxReader = new SAXReader(); Document document=null; try URL xmlpath = this.getClass().getClassLoader().getResource(filename); document = saxReader.read(xmlpath); Map nsMap = new HashMap(); nsMap.put(“ns“,“http:/www.springframework.org/schema/beans“);/加入命名空间 X
18、Path xsub = document.createXPath(“/ns:beans/ns:bean“);/创建 beans/bean查询路径 xsub.setNamespaceURIs(nsMap);/设置命名空间 List beans = xsub.selectNodes(document);/获取文档下所有 bean节点 for(Element element: beans) String id = element.attributeValue(“id“);/获取 id属性值 String clazz = element.attributeValue(“class“); /获取 cla
19、ss属性值 BeanDefinition beanDefine = new BeanDefinition(id, clazz); beanDefines.add(beanDefine); catch(Exception e) e.printStackTrace(); 三种实例化 bean的方式 1.使用类构造器实例化 2.使用静态工厂方法实例化 public class OrderFactory public static OrderServiceBean createOrder() return new OrderServiceBean(); 3.使用实例工厂方法实例化 : public c
20、lass OrderFactory public OrderServiceBean createOrder() return new OrderServiceBean(); Bean的作用域 .singleton 在每个 Spring IoC容器中一个 bean定义只有一个对象实例。默认情况下会在容器启动时初始化 bean,但我们可以指定 Bean节点的 lazy-init=“true”来延迟初始化 bean, 这时候,只有第一次获取 bean会才初始化 bean。如: 如果想对所有 bean都应用延迟初始化,可以在根节点 beans设置 default-lazy-init=“true“, 如
21、下: .prototype 每次从容器获取 bean都是新的对象。 .request .session .global session 指定 Bean的初始化方法和销毁方法 指定 Bean的初始化方法和销毁方法 注入依赖对象 基本类型对象注入: /构造器注入 /属性 setter方法注入 注入其他 bean: 方式一 方式二 (使用内部 bean,但该 bean不能被其他 bean使用 ) 集合类型的装配 public class OrderServiceBean private Set sets = new HashSet(); private List lists = new ArrayL
22、ist(); private Properties properties = new Properties(); private Map maps = new HashMap(); /这里省略属性的 getter和 setter方法 集合类型的装配 lihuoming set sss 依赖注入 使用构造器注入 使用属性 setter方法注入 使用 Field注入(用于注解方式) 注入依赖对象可以采用 手工装配 或 自动装配 ,在实际应用中建议使用手工装配,因为自动装配会产生未知情况 ,开发人员无法预见最终的装配结果。 1.手工装配依赖对象 2.自动装配依赖对象 依赖注入 -手工装配 手工装配依
23、赖对象,在这种方式中又有两种编程方式 1. 在 xml配置文件中,通过在 bean节点下配置,如 /构造器注入 /属性 setter方法注入 2. 在 java代码中使用 Autowired或 Resource注解方式进行装配。但我们需要在 xml配置文件中配置以下信息: 这个配置隐式注册了多个对注释进行解析处理的处理器 :AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPost
24、Processor 注: Resource注解在 spring安装目录的 libj2eecommon-annotations.jar 依赖注入 -手工装配 在 java代码中使用 Autowired或 Resource注解方式进行装配,这两个注解的区别是:Autowired 默认按类型装配 , Resource默认按名称装配,当找不到与名称匹配的 bean才会按类型装配。 Autowired private PersonDao personDao;/用于字段上 Autowired public void setOrderDao(OrderDao orderDao) /用于属性的 setter方
25、法上 this.orderDao = orderDao; Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许 null值,可以设置它 required属性为 false。如果我们想使用按名称装配,可以结合 Qualifier注解一起使用。如下: Autowired Qualifier(“personDaoBean“) private PersonDao personDao; Resource注解和 Autowired一样,也可以标注在字段或属性的 setter方法上,但它默认按名称装配。名称可以通过Resource的 name属性指定,如果没有指定 name
26、属性,当注解标注在字段上,即默认取字段的名称作为 bean名称寻找依赖对象,当注解标注在属性的 setter方法上,即默认取属性名作为 bean名称寻找依赖对象。 Resource(name=“personDaoBean”) private PersonDao personDao;/用于字段上 注意:如果没有指定 name属性,并且按照默认的名称仍然找不到依赖对象时, Resource注解会回退到按类型装配。但一旦指定了 name属性,就只能按名称装配了。 依赖注入 -自动装配依赖对象 对于自动装配,大家了解一下就可以了,实在不推荐大家使用。例子: autowire属性取值如下: byType
27、: 按类型装配,可以根据属性的类型,在容器中寻找跟该类型匹配的 bean。如果发现多个,那么将会抛出异常。如果没有找到,即属性值为 null。 byName: 按名称装配,可以根据属性的名称,在容器中寻找跟该属性名相同的 bean,如果没有找到,即属性值为 null。 constructor与 byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的 bean,那么将会抛出异常。 autodetect:通过 bean类的自省机制( introspection)来决定是使用 constructor还是byType方式进行自动装配。 如果发现默认的构造器,
28、那么将使用 byType方式 。 通过在 classpath自动扫描方式把组件纳入 spring容器中管理 前面的例子我们都是使用 XML的 bean定义来配置组件。在一个稍大的项目中,通常会有上百个组件,如果这些这组件采用 xml的 bean定义来配置 , 显然会增加配置文件的体积 , 查找及维护起来也不太方便 。 spring2.5为我们引入了组件自动扫描机制,他可以在类路径底下寻找标注了 Component、Service、 Controller、 Repository注解的类,并把这些类纳入进 spring容器中管理 。 它的作用和在 xml文件中使用 bean节点配置组件是一样的 。
29、 要使用自动扫描机制,我们需要打开以下配置信息 : 其中 base-package为需要扫描的包 (含子包 )。 Service用于标注业务层组件、 Controller用于标注控制层组件(如 struts中的 action)、Repository用于标注数据访问组件,即 DAO组件。而 Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。 JDK动态代理 public class JDKProxy implements InvocationHandler private Object targetObject;/代理的目标对象 public Object crea
30、teProxyInstance(Object targetObject) this.targetObject = targetObject; /* * 第一个参数设置代码使用的类装载器 ,一般采用跟目标类相同的类装载器 * 第二个参数设置代理类实现的接口 * 第三个参数设置回调对象 ,当代理对象的方法被调用时 ,会委派给该参数指定对象的 invoke方法 */ return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterface
31、s(), this); public Object invoke(Object proxy, Method method, Object args) throws Throwable return method.invoke(this.targetObject, args);/把方法调用委派给目标对象 当目标类实现了接口,我们可以使用 jdk的 Proxy来生成代理对象。 使用 CGLIB生成代理 public class CGLIBProxy implements MethodInterceptor private Object targetObject;/代理的目标对象 public Ob
32、ject createProxyInstance(Object targetObject) this.targetObject = targetObject; Enhancer enhancer = new Enhancer();/该类用于生成代理对象 enhancer.setSuperclass(this.targetObject.getClass();/设置父类 enhancer.setCallback(this);/设置回调用对象为本身 return enhancer.create(); public Object intercept(Object proxy, Method metho
33、d, Object args, MethodProxy methodProxy) throws Throwable return methodProxy.invoke(this.targetObject, args); CGLIB可以生成目标类的子类,并重写父类非 final修饰符的方法。 AOP中的概念 Aspect(切面 ):指横切性关注点的抽象即为切面 ,它与类相似 ,只是两者的关注点不一样 ,类是对物体特征的抽象 ,而切面横切性关注点的抽象 . joinpoint(连接点 ):所谓连接点是指那些被拦截到的点。在 spring中 ,这些点指的是方法 ,因为 spring只支持方法类型的连
34、接点 ,实际上 joinpoint还可以是 field或类构造器 ) Pointcut(切入点 ):所谓切入点是指我们要对那些 joinpoint进行拦截的定义 . Advice(通知 ):所谓通知是指拦截到 joinpoint之后所要做的事情就是通知 .通知分为前置通知 ,后置通知 ,异常通知 ,最终通知 ,环绕通知 Target(目标对象 ):代理的目标对象 Weave(织入 ):指将 aspects应用到 target对象并导致 proxy对象创建的过程称为织入 . Introduction(引入 ):在不修改类代码的前提下 , Introduction可以在运行期为类动态地添加一些方法或 Field.