发表于: 2020-05-04 23:17:52

1 1263


6.Spring的IOC有几种方式?它们之间的差别是什么,应该选择Annonation还是应该选择XML?

以下内容摘自:Spring依赖注入的四种方式

IOC (Inversion of Control)控制反转,是一种设计思想。在 java 开发中, IOC 意味着把设计好的对象交给容器管理,而不是传统的在对象内部直接控制。


传统的 java se 程序设计都是通过 new 来创建对象;IOC 则是有一个专门的容器来控制对象的创建;主要是控制外部资源的获取(对象,容器等)


传统的应用程序都是由我们在程序中直接获取依赖对象,这被称为正转;反转就是由容器来帮忙查找、创建以及注入依赖对象,而我们是被动的接受依赖对象,所以是正转;是依赖对象的获取被反转了。

传统开发:


IOC:


在 spring 中有四种依赖注入,分别是:

a 接口注入

b setter 方法注入

c 构造方法注入

d 注解注入


接口注入(没用过,看不懂):

public class ClassA {
   private InterfaceB clzB;
   public void doSomething() {
     Ojbect obj = Class.forName(Config.BImplementation).newInstance();
     clzB = (InterfaceB)obj;
     clzB.doIt();
   }
 ……


setter 注入(在做 service 与 dao 分开的时候用过,下例摘自当时项目的代码):

spring 配置文件:

<!-- 配置数据库相关参数properties的属性:${url} -->
<context:property-placeholder location="classpath:db.properties" />
<!--配置c3p0连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

<!--配置sqlSqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入mybatis核心配置文件-->
   <property name="configLocation" value="mybatis-config.xml"></property>
<!--注入数据源-->
   <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- dao 注入上面配置好的 sqlSqlSessionFactory-->
<bean id="discipleDao" class="cn.mogeek.dao.DiscipleDaoImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>

<!-- service 注入上面配置好的 dao-->
<bean id="service" class="cn.mogeek.service.ServiceImpl">
<property name="discipleDao" ref="discipleDao"></property>
</bean>

serviceImpl.java

public class ServiceImpl implements Service {

private DiscipleDao discipleDao;

public void setDiscipleDao(DiscipleDao discipleDao){ this.discipleDao = discipleDao; }

public DiscipleDao getDiscipleDao(){ return discipleDao; }

···

}


构造器注入:

spring 配置文件(配置文件和 setter 一样,只是在代码中表示依赖的方式不同):

<?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:aop="http://www.springframework.org/schema/aop"                 xmlns:tx="http://www.springframework.org/schema/tx"                 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd                   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd                   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
      <!-- 使用spring管理对象的创建,还有对象的依赖关系 -->
     <bean id="userDao4Mysql" class="com.tgb.spring.dao.UserDao4MysqlImpl"/>          <bean id="userDao4Oracle" class="com.tgb.spring.dao.UserDao4OracleImpl"/>         <bean id="userManager" class="com.tgb.spring.manager.UserManagerImpl">           <!-- (1)userManager使用了userDao,Ioc是自动创建相应的UserDao实现,都是由容器管理-->
     <!-- (2)在UserManager中提供构造函数,让spring将UserDao实现注入(DI)过来 -->
     <!-- (3)让spring管理我们对象的创建和依赖关系,必须将依赖关系配置到spring的核心配置文件中 -->
           <constructor-arg ref="userDao4Oracle"/>
       </bean>
 </beans>

构造器表示依赖关系的写法:

import com.tgb.spring.dao.UserDao;
  public class UserManagerImpl implements UserManager{
      private UserDao userDao;
                   //使用构造方式赋值
      public UserManagerImpl(UserDao userDao) {
               this.userDao = userDao;
      }
      @Override
      public void addUser(String userName, String password) {                              userDao.addUser(userName, password);
      }
   }


注解方式(这种方式我用过但比较陌生):

spring 中注入依赖可以分为手工装配与自动装配,实际开发中建议使用手工装配,因为自动装配会产生无法预见的结果。


手工装配又分为两种方式:

(a)在 spring 配置文件中,通过 bean 节点配置,然后在代码中用 setter 或者构造器方法注入。

(b)在 java 代码中使用注解的方式进行装配,在代码中加入 @Resource 或者 @Autowired 

             Autowired 是自动注入,自动在 spring 上下文中找到合适的 bean 来注入

             Resource 用来指定名称注入

             Qualifier 和 Autowires 配合使用,指定 bean 名称,如下:

@Autowired
@Qualifier("userDAO")
private UserDAO userDAO;


spring 配置文件:

<?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:p="http://www.springframework.org/schema/p"    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    ">
    <beans>
     ……
     <context:annotation-config></context:annotation-config>
     ……
     </beans>  
   <bean id="userDao" class="com.springtest.dao.impl.UserDAOImpl"></bean>
   <bean id="userBiz" class="com.springtest.biz.impl.UserBizImpl"></bean>
</beans>


在需要注入依赖的类中,声明一个依赖对象,并且添加注释:

public class UserBizImpl implements UserBiz {
    @Resource(name="userDao")
    private UserDAO userDao = null;
    public void addUser() {
           this.userDao.addUser();
    }
 }



区别:

接口注入:具备侵入性,要求组件必须与特定的接口想关联,因此不被看好,实际使用有限。

setter注入:用 setter 方法设定依赖关系比较直观,如果依赖关系较为复杂,那么构造子注入模式的构造函数也会相当庞大。

构造器注入:所有的依赖关系在构造函数中集中体现,组件一旦创建则处于稳定状态。

注解注入:不用在配置文件中配置大量的依赖对象,易于维护。


实际使用时更推荐使用注解注入。


7..JDBCTemplate和JDBC

JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API, 可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。


JDBCTemplate 是 Spring 对 JDBC 的封装,通俗点说就是 Spring 对 jdbc 的封装的模板, Spring 对数据库的操作在 jdbc 上面做了深层次的封装,使用 spring 的注入功能,可以把DataSource 注入到 JdbcTemplate 之中。


8.Spring中的IOC是什么意思,为什么要用IOC而不是New来创建实例?

使用 new 来创建实例有以下弊端:

使用起来非常繁琐,而且需要了解整个依赖链条并全部实例化。

当之前的依赖发生改变的时候之后所有的实例都需要修改。这导致了耦合非常紧密,不利于业务的开发。

这是一种硬编码,违反了面向接口编程的原则。

频繁创建对象,浪费了资源。


使用 IOC 之后,只管向容器索取 bean 即可:

bean 之间解耦,在运行期间会动态的进行依赖设置。

需要更改 dao 的时候只需要修改 daoImpl,不会破坏其他的代码。

不需要手动整理其依赖关系。


9.为什么要使用Interface,而不是直接使用一个实体类来完成任务?Interface和Impl这种方式的好处是什么?

为什么使用 interface:

1.定义了一个统一的接口,方便开发人员并行合作。

2.代码简洁,便于查看

3.需要修改的时候只需要修改其实现,对外暴露的接口不用变


10.为什么要处理异常,Try/Catch应该在什么样的场景下使用,在真实的系统中,会出现网络中断,DB连接不上的错误吗?多久会发 生一次?

在实际的运行环境中,总有开发者无法预料甚至无关管控的情况发生,为了不让这些意外情况产生不可预测的后果,我们需要主动处理异常。

另外,有时候一个模块出现的错误在本模块中无法进行处理,需要抛出给调用它的模块来处理,这个时候也就需要调用模块处理异常。


11.日志应该怎么打,在什么位置,需要打印出来什么样的关键参数?

打印日志应该使用框架,而不是 System.out.print``` ,否则日志不会保存下来,不利于排错。

应该选择正确的日志级别,Log最常用的级别就是DEBUG,INFO,WARN,ERROR。


需要打印的关键信息:

时间、类名及函数名

重要函数的开始结束

重要函数或web接口的每个返回分支

堆栈信息



12.为什么需要单步调试?Debug的时候IDE是怎么找到源码的?

单步调试可以随意的暂停,查看上下文变量的值来分析具体错误产生的原因。


(IDE 怎么找到源码的不太清楚,只找到了一些原理)

java 的 debug 依赖于 JPDA(Java Platform Debugger Architecture)java 平台调试体系结构。

java 程序运行在 JVM 之上,调试的时候实际上就是向 JVM 来请求当前程序运行的状态,JPDA 就是虚拟机提供的一套 java 调试工具与接口。

JPDA 分为三个部分,分别是Java虚拟机工具接口(JVMTI),Java调试线协议(JDWP)以及Java调试接口(JDI)。


13.可否远程连接到线上直接调试?真实的项目中,遇到问题的排查方案是什么?

可以做到,但是不应该这么做。

以下内容摘自:排查Java线上服务故障的方法和实例分析

排查方案:

(1) 紧急处理 :立刻追查最近线上系统是否有更改,立刻回滚更新

(2) 添加监控 

(3) 使用JDK性能监控工具 :

                  (1)首先要查看日志,看看有没有Exception。另外日志中常常有对接口调用,缓存使用的监控告警信息。 

                   (2)看看目前gc的状况如何,使用JDK自带的工具就可以。jstat -gc -h 10 vmid 5000,每5秒打出一次。我遇到过两次线上故障,都是简单的通过jstat就找到了问题。一次是Permanent区分配过小,JVM内存溢出。另一次是发生了过多的Full GC,使得系统停止响应。内存造成的问题通过简单的重启服务就可以使得服务恢复,但重启之后往往很快又出现问题。这个期间你要监控gc,看看期间发生了什么事情。如果是old区增长的过快,就可能是内存泄露。这个时候,你需要看看到底是什么对象占用了你的内存。

                     (3)jmap -histo vmid > jmap.log,该命令会打出所有对象,包括占用的byte数和实例个数。

(4) 分析源代码。从治标不治本,到治标又治本。




返回列表 返回列表
评论

    分享到