spring巧用继承解决bean的id相同的问题
bean名称相同冲突Annotation-specifiedbeannamexxforbe。。。
根据bean名称在ide中查找找到这两个重名的类可以看到由于这两个类使用service标注此时如果不使用命名那么spring会在扫描时将类名首字母小写作为key放到一个全局map中维护
bean名称相同冲突 Annotation-specifiedbeannamexxforbe。。 。
工程中引入其他工程的包,由于两个工程中有重名的两个bean,导致在启动时提示如下错误:
谈谈spring-boot不同包结构下,同样的类名冲突导致服务启动失败解决方案
谈谈spring-boot不同包结构下,同样的类名冲突导致服务启动失败解决⽅案项⽬背景: 某⽇,有需求要在三天的时间内完成两个⼤项⽬的项⽬合并,因为之前两个项⽬的包结构和类名都很多相同,于是开始考虑使⽤加⼀级包进⾏隔离,类似于这种结构但是在启动的过程中,抛出来这样的异常:Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'nameConflict' for bean class [Conflict] conflicts with existing, non-compatible b at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.checkCandidate(ClassPathBeanDefinitionScanner.java:348) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:286) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]at ponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:132) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:284) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:241) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:198) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:166) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]... 13 common frames omitted。
spring中beanid相同引发故障的分析与解决
spring中beanid相同引发故障的分析与解决前⾔最近因为同事bean配置的问题导致⽣产环境往错误的redis实例写⼊⼤量的数据,差点搞挂redis。
经过快速的问题定位,发现是同事新增⼀个redis配置⽂件,并且配置的RedisSentinelConfiguration的id是⼀样的,然后在使⽤@Autowired注⼊bean的时候因为spring bean覆盖的机制导致读取的redis配置不是原来的。
总结起来,有两点问题:为什么相同bean id的bean会被覆盖@Autowired注解不是按照byType的⽅式进⾏注⼊的吗代码如下:public class UserConfiguration {private int id;private String name;private String city;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) { = name;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}}UserClient:public class UserClient {private UserConfiguration configuration;public UserClient(UserConfiguration configuration) {this.configuration = configuration;}public String getCity() {return configuration.getCity();}}beans.xml:<?xml version="1.0" encoding="UTF-8"?><beans xmlns="/schema/beans"xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/schema/beans/schema/beans/spring-beans.xsd"><bean id="userConfiguration" class="erConfiguration"><property name="id" value="${user1.id}"/><property name="name" value="${}"/><property name="city" value="${user1.city}"/></bean><bean id="userClient" class="erClient" autowire="byName"><constructor-arg ref="userConfiguration"/></bean></beans>beans2.xml:<?xml version="1.0" encoding="UTF-8"?><beans xmlns="/schema/beans"xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/schema/beans/schema/beans/spring-beans.xsd"><bean id="userConfiguration" class="erConfiguration"><property name="id" value="${user2.id}"/><property name="name" value="${}"/><property name="city" value="${user2.city}"/></bean><bean id="userClient2" class="erClient"><constructor-arg ref="userConfiguration"/></bean></beans>application.properties:user1.id=1=bean1user1.city=Hangzhouuser2.id=2=bean2user2.city=ShanghaiApplition:@SpringBootApplicationpublic class Application{@AutowiredUserClient userClient2;@PostConstructpublic void init() {String city = userClient2.getCity();System.out.println(city);}public static void main(String[] args) throws InterruptedException {SpringApplication.run(Application.class, args);Thread.sleep(Long.MAX_VALUE);}}运⾏程序,你会发现不管注⼊的userClient2还是userClient1,输出的结果都是Shanghai。
spring原理机制
spring原理机制转⾃:/nrain2/article/details/454593111,关于spring容器:spring容器是Spring的核⼼,该容器负责管理spring中的java组件,ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");//这种⽅式实例化容器,容器会⾃动预初始化所有Bean实例ctx.getBean("beanName");ApplicationContext 实例正是Spring容器。
ApplicationContext容器默认会实例化所有的singleton BeanSpring容器并不强制要求被管理组件是标准的javabean。
2,Spring的核⼼机制:依赖注⼊。
不管是依赖注⼊(Dependency Injection)还是控制反转(Inversion of Conctrol),其含义完全相同:当某个java实例(调⽤者)需要调⽤另⼀个java实例(被调⽤者)时,传统情况下,通过调⽤者来创建被调⽤者的实例,通常通过new来创建,⽽在依赖注⼊的模式下创建被调⽤者的⼯作不再由调⽤者来完成,因此称之为"控制反转";创建被调⽤者实例的⼯作通常由Spring来完成,然后注⼊调⽤者,所以也称之为"依赖注⼊"。
3,依赖注⼊⼀般有2中⽅式:设置注⼊:IoC容器使⽤属性的setter⽅式注⼊被依赖的实例。
<property name="" ref="">构造注⼊:IoC容器使⽤构造器来注⼊被依赖的实例。
<constructor-arg ref="">配置构造注⼊的时候<constructor-arg>可以配置index属性,⽤于指定该构造参数值作为第⼏个构造参数值。
SSM框架练习题
SSM框架练习题第⼀套⼀、填空题1.Spring是以IoC和【】为内核答案:AOP2.在Spring配置⽂件中,Bean的作⽤域是通过元素的【】属性来指定的。
答案:scope3.【】注解⽤于将数据访问层(DAO层)的类标识为Spring中的Bean,其功能与@Component 相同。
答案:@Repository4.AspectJ框架中注解【】⽤于定义切⼊点表达式,在使⽤时还需定义⼀个包含名字和任意参数的⽅法签名来表⽰切⼊点名称答案:@Pointcut5.Spring中的通知按照在⽬标类⽅法的连接点位置,可以分为以下5种类型:【】、前置通知、后置通知、异常通知、引介通知。
答案:环绕通知6.JDBC连接数据库时需要4个基本属性,包括有【】、url、username和password。
答案:driverClassName7.Spring的声明式事务管理可以通过两种⽅式来实现,⼀种是基于XML的⽅式,另⼀种是基于【】的⽅式。
答案:Annotation8.MyBatis⼊门程序中可以使⽤SqlSession的【】⽅法执⾏更新操作。
答案:update()9.当数据表中的列和需要返回的对象的属性不完全⼀致, MyBatis是不会⾃动赋值的。
此时,就可以使⽤【】元素进⾏处理。
答案:resultMap10.SQL语句中,where后直接跟and,这在运⾏时肯定会报【】错误。
答案:SQL语法⼆、判断题11.当Bean的作⽤域为singleton时,Spring容器就只会存在⼀个共享的Bean实例,并且所有对Bean的请求,只要id与该Bean的id属性相匹配,就会返回同⼀个Bean实例()。
答案:正确12.对于使⽤业务接⼝的类,Spring默认会使⽤CGLIB动态代理来实现AOP()。
答案:错误13.JdbcTemplate类中的update()⽅法可以完成查询、插⼊、更新和删除数据的操作()。
答案:错误14.MyBatis映射⽂件中<insert>元素⽤于映射插⼊语句,在执⾏完元素中定义的SQL语句后,没有返回结果()答案:错误15.MyBaits与Spring进⾏整合时,Dao层开发可以使⽤传统的DAO⽅式的开发整合,以及Mapper接⼝⽅式的开发整合()。
基于spring同名bean覆盖问题的解决
基于spring同名bean覆盖问题的解决⽬录spring同名bean覆盖问题分为两种情况处理案例如下spring ⼦类覆盖⽗类中注⼊的bean抽象基类另外⼀个抽象基类controllerspring同名bean覆盖问题默认情况下,spring在处理同⼀个ApplicationContext中名称相同的bean时分为两种情况处理1、如果两个bean是在同⼀个配置⽂件中,那么spring会报错。
2、如果两个bean是在不同的配置⽂件中,默认情况下,spring会覆盖先前的bean。
在配置⽂件很多时,如果在启动时,对于同名的bean加载没有异常信息,出现问题后会⽐较难以定位。
在spring中,处理容器的元数据信息时,默认使⽤DefaultListableBeanFactory类,该类中有个属性:allowBeanDefinitionOverriding,默认情况下为true,即允许重名的bean可以被覆盖。
还好,spring有办法对改属性赋值。
重写ContextLoaderListener,对于web应⽤,容器类型为XmlWebApplicationContext,在该类中设置allowBeanDefinitionOverriding为false,然后在spring启动时,碰到同名bean就会抛出异常。
案例如下public class TradeContextLoaderListener extends ContextLoaderListener {@Overrideprotected void customizeContext(ServletContext servletContext,ConfigurableWebApplicationContext applicationContext) {super.customizeContext(servletContext, applicationContext);XmlWebApplicationContext context = (XmlWebApplicationContext) applicationContext;context.setAllowBeanDefinitionOverriding(false);}}配置web.xml:<listener><description>spring监听器</description><listener-class>com.***.trade.system.web.util.TradeContextLoaderListener</listener-class></listener>spring ⼦类覆盖⽗类中注⼊的bean我们在设计程序框架的时候,会设计⼀个抽象基类,⼦类继承这个基类,共有的⽅法放到基类中去,使⽤spring后使代码变的很简单,现在遇到的问题是在基类中注⼊bean后,⼦类不可能都会是有这个bean,那么需要考虑到⼦类需要覆盖或者说重新注⼊个性化的bean有三种⽅法来实现这个效果,以下是⼀种⽅法,如下⾯代码:抽象基类public abstract class AbstractNameService{public abstract String getname();}两个实现类:@Service("firstNameService")public class FirstNameService extends AbstractNameService{@Overridepublic String getname(){return "FirstName";}}@Service("nameService")public class NameService extends AbstractNameService{@Overridepublic String getname(){return "Name";}}另外⼀个抽象基类public abstract class AbstractService{protected AbstractNameService nameService;public String getName(){return nameService.getname();}public AbstractNameService getService(){return nameService;}<span style="color:#ff9966;">@Resource(name = "nameService")</span>public void setService(AbstractNameService nameService){Service = nameService;}}实现类:@Service("getNameService")public class GetNameService extends AbstractService{<span style="color:#ff9900;">@Resource(name = "firstNameService")</span> @Overridepublic void setService(AbstractNameService nameService){Service = nameService;}}controller@Controllerpublic class UnionpayQuickPayDSMVC{@Resourceprivate AbstractService getNameService;@RequestMapping(value = "/*", method = RequestMethod.GET)public void execute(HttpServletRequest request, HttpServletResponse response) {try{response.getWriter().write(getNameService.getName());}catch (IOException e){System.out.println(e);}}}在applicationContext.xml和springmvc的配置⽂件只需要添加⼀个包<context:component-scan/>标签就⾏了以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。
Spring的常见问题及答案
Spring的常见问题及答案⽬录Spring 概述依赖注⼊Spring beansSpring注解Spring数据访问Spring⾯向切⾯编程(AOP)Spring MVCSpring 概述1. 什么是spring?Spring 是个java企业级应⽤的开源开发框架。
Spring主要⽤来开发Java应⽤,但是有些扩展是针对构建J2EE平台的web应⽤。
Spring 框架⽬标是简化Java企业级应⽤开发,并通过POJO为基础的编程模型促进良好的编程习惯。
2. 使⽤Spring框架的好处是什么?轻量:Spring 是轻量的,基本的版本⼤约2MB。
控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,⽽不是创建或查找依赖的对象们。
⾯向切⾯的编程(AOP):Spring⽀持⾯向切⾯的编程,并且把应⽤业务逻辑和系统服务分开。
容器:Spring 包含并管理应⽤中对象的⽣命周期和配置。
MVC框架:Spring的WEB框架是个精⼼设计的框架,是Web框架的⼀个很好的替代品。
事务管理:Spring 提供⼀个持续的事务管理接⼝,可以扩展到上⾄本地事务下⾄全局事务(JTA)。
异常处理:Spring 提供⽅便的API把具体技术相关的异常(⽐如由JDBC,Hibernate or JDO抛出的)转化为⼀致的unchecked 异常。
3. Spring由哪些模块组成 ?以下是Spring 框架的基本模块:Core moduleBean moduleContext moduleExpression Language moduleJDBC moduleORM moduleOXM moduleJava Messaging Service(JMS) moduleTransaction moduleWeb moduleWeb-Servlet moduleWeb-Struts moduleWeb-Portlet module4. 核⼼容器(应⽤上下⽂) 模块。
springboot-启动bean冲突的解决
springboot-启动bean冲突的解决⽬录启动bean冲突启动提⽰bean重复问题先说结论原理启动bean冲突在⼀次启动中遇到了bean冲突的问题,提⽰存在两个名称重复的beanorg.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class[com.test.api.Application]; nested exception isorg.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'healthCheckController' for bean class [com.test.datahub.controller.HealthCheckController] conflicts with existing, non-compatible bean definition of same name and class [com.test.api.controller.HealthCheckController]项⽬中包括多个模块,其中A、B两个模块都有同⼀个类:HealthCheckController,检查更改信息发现,不知道谁在A模块添加了B模块的依赖,造成了这⼀问题,删除后解决<dependency><groupId>com.test</groupId><artifactId>B</artifactId><version>1.0.0-SNAPSHOT</version></dependency>启动提⽰bean重复问题先说结论只需要在@FeignClient注解的contextId属性上加上独⼀的标⽰,即可解决问题原理是因为注册feignClient的时候会注册ClientConfiguration,参考代码如下public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {//...此处省略部分代码//for (String basePackage : basePackages) {Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);for (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {// verify annotated class is an interface//...省略部分代码//这块是把注解上的所有属性封装到map上Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());//这两个重点⽅法请看下⾯代码块//获取该feignClient的名字(重点关注该⽅法)String name = getClientName(attributes);//此⽅法就是spring注⼊beanDefination的步骤(重点关注该⽅法)registerClientConfiguration(registry, name,attributes.get("configuration"));registerFeignClient(registry, annotationMetadata, attributes);}}}}上⾯的两处重点⽅法,请看此处//@param client 这个map就是通过上⾯的注解属性转map得到的private String getClientName(Map<String, Object> client) {if (client == null) {return null;}//如果从contextId获取到名字,那么value有值的String value = (String) client.get("contextId");//如果value有值,那么如下3个if条件都不会⾛,所以contextId唯⼀就可以做到bean不重复,//如果value没有值,就会取value,因为多个client的serverName都是⼀样的,那么就会出现重复beanif (!StringUtils.hasText(value)) {value = (String) client.get("value");}if (!StringUtils.hasText(value)) {value = (String) client.get("name");}if (!StringUtils.hasText(value)) {value = (String) client.get("serviceId");}if (StringUtils.hasText(value)) {return value;}throw new IllegalStateException("Either 'name' or 'value' must be provided in @"+ FeignClient.class.getSimpleName());}private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,Object configuration) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);builder.addConstructorArgValue(name);builder.addConstructorArgValue(configuration);//在这个位置,创建beanDefinition,但是他创建的名字格式可以看出,唯⼀改变变量就是name,这个name就是上⾯看到的那个⽅法获取的 registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),builder.getBeanDefinition());}以上就是feign导致的springBean重复问题的解释,仅上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。
Spring循环依赖的三种方式以及解决办法
Spring循环依赖的三种⽅式以及解决办法⼀. 什么是循环依赖?循环依赖其实就是循环引⽤,也就是两个或者两个以上的bean互相持有对⽅,最终形成闭环。
⽐如A依赖于B,B依赖于C,C⼜依赖于A。
如下图:注意,这⾥不是函数的循环调⽤,是对象的相互依赖关系。
循环调⽤其实就是⼀个死循环,除⾮有终结条件。
Spring中循环依赖场景有:(1)构造器的循环依赖(2)field属性的循环依赖其中,构造器的循环依赖问题⽆法解决,只能拋出BeanCurrentlyInCreationException异常,在解决属性循环依赖时,spring采⽤的是提前暴露对象的⽅法。
⼆. 怎么检测是否存在循环依赖检测循环依赖相对⽐较容易,Bean在创建的时候可以给该Bean打标,如果递归调⽤回来发现正在创建中的话,即说明了循环依赖了。
三、三种循环依赖1:构造器的循环依赖。
【这个Spring解决不了】 Spring容器会将每⼀个正在创建的Bean 标识符放在⼀个“当前创建Bean池”中,Bean标识符在创建过程中将⼀直保持在这个池中,因此如果在创建Bean过程中发现⾃⼰已经在“当前创建Bean池”⾥时将抛出BeanCurrentlyInCreationException异常表⽰循环依赖;⽽对于创建完毕的Bean将从“当前创建Bean池”中清除掉。
Spring容器先创建单例A,A依赖B,然后将A放在“当前创建Bean池”中,此时创建B,B依赖C ,然后将B放在“当前创建Bean池”中,此时创建C,C⼜依赖A,但是,此时A已经在池中,所以会报错,因为在池中的Bean都是未初始化完的,所以会依赖错误,(初始化完的Bean会从池中移除)public class StudentA {private StudentB studentB ;public void setStudentB(StudentB studentB) {this.studentB = studentB;}public StudentA() {}public StudentA(StudentB studentB) {this.studentB = studentB;}}public class StudentB {private StudentC studentC ;public void setStudentC(StudentC studentC) {this.studentC = studentC;}public StudentB() {}public StudentB(StudentC studentC) {this.studentC = studentC;}}public class StudentC {private StudentA studentA ;public void setStudentA(StudentA studentA) {this.studentA = studentA;}public StudentC() {}public StudentC(StudentA studentA) {this.studentA = studentA;}}上⾯是很基本的3个类,,StudentA有参构造是StudentB。
同名bean覆盖原则
同名bean覆盖原则同名bean覆盖原则是指在Spring框架中,当存在多个同名的bean定义时,后面定义的bean会覆盖前面定义的bean。
这个原则在Spring的IoC容器中起到了重要的作用,可以确保在同一个应用程序中只存在一个相同名称的bean,避免了bean名称冲突的问题。
同名bean覆盖原则可以分为两个方面来进行解释:同名bean 的定义和同名bean的覆盖。
首先,同名bean的定义是指在Spring的配置文件中,可以定义多个具有相同名称的bean。
例如,可以在XML配置文件中使用<bean>标签来定义多个相同名称的bean,或者在注解配置中使用@Component或者@Service等注解来定义多个相同名称的bean。
这样的定义方式是合法的,因为Spring允许使用不同的配置方式来定义bean。
其次,同名bean的覆盖是指当存在多个同名的bean定义时,后面定义的bean会覆盖前面定义的bean。
这意味着在同一个应用程序中,只会存在一个同名的bean实例。
当Spring容器初始化时,在遇到同名bean定义的情况下,会使用后面定义的bean来覆盖前面定义的bean。
这样做的好处是可以确保同一个应用程序中的bean名称是唯一的,避免了bean名称冲突的问题。
同名bean的覆盖原则可以使开发者更加方便地管理和使用bean。
当存在多个同名bean定义时,开发者可以只关注最后一个定义的bean,而不需要关心前面的定义。
这样可以简化配置文件的维护和管理,提高代码的可读性和可维护性。
需要注意的是,同名bean的覆盖原则只适用于同一个应用程序中的bean定义。
如果存在多个应用程序,每个应用程序都有自己的Spring容器,则同名bean的覆盖原则不会生效。
在这种情况下,不同的应用程序可以定义相同名称的bean而不会相互影响。
总结起来,同名bean覆盖原则是Spring框架中的一项重要特性,它确保了在同一个应用程序中只存在一个相同名称的bean。
springmvc中自己实现的token防表单重复提交,防止二次提交
springmvc中自己实现的token防表单重复提交,防止二次提交代码简介springmvc中自己实现的token防表单重复提交,防止二次提交代码片段一:首先创建一个token处理类,这里的类名叫 T okenHandlerprivate static Logger logger = Logger.getLogger(TokenHandler.class);static Map<String, String> springmvc_token = new HashMap<String, String>();//生成一个唯一值的token@SuppressWarnings("unchecked")public synchronized static String generateGUID(HttpSession session) {String token = "";try {Object obj = session.getAttribute("SPRINGMVC.TOKEN");if(obj!=null)springmvc_token = (Map<String,String>)session.getAttribute("SPRINGMVC.TOKEN" );token = new BigInteger(165, new Random()).toString(36).toUpperCase();springmvc_token.put(Constants.DEFAULT_TOKEN_NAME + "." + token,token);session.setAttribute("SPRINGMVC.TOKEN",springmvc_token);Constants.TOKEN_VALUE = token;} catch (IllegalStateException e) {logger.error("generateGUID() mothod find bug,by token session...");}return token;}//验证表单token值和session中的token值是否一致@SuppressWarnings("unchecked")public static boolean validToken(HttpServletRequest request) {String inputT oken = getInputT oken(request);if (inputToken == null) {logger.warn("token is not valid!inputT oken is NULL");return false;}HttpSession session = request.getSession();Map<String, String> tokenMap = (Map<String, String>) session.getAttribute("SPRINGMVC.TOKEN");if (tokenMap == null || tokenMap.size() < 1) {logger.warn("token is not valid!sessionToken is NULL");return false;}String sessionT oken =tokenMap.get(Constants.DEFAULT_TOKEN_NAME + "."+ inputToken);if (!inputToken.equals(sessionT oken)) {logger.warn("token is not valid!inputT oken='" + inputToken + "',sessionT oken = '" + sessionToken + "'");return false;}tokenMap.remove(Constants.DEFAULT_TOKEN_NAME + "." + inputToken);session.setAttribute("SPRINGMVC.TOKEN", tokenMap);return true;}//获取表单中token值@SuppressWarnings("unchecked")public static String getInputT oken(HttpServletRequest request) {Map params = request.getParameterMap();if (!params.containsKey(Constants.DEFAULT_TOKEN_NAME)) {logger.warn("Could not find token name in params.");return null;}String[] tokens = (String[]) (String[]) params.get(Constants.DEFAULT_TOKEN_NAME);if ((tokens == null) || (tokens.length < 1)) {logger.warn("Got a null or empty token name.");return null;}return tokens[0];}代码片段三:这是我用到的常量:public static String DEFAULT_TOKEN_MSG_JSP = "unSubmit.jsp" ;public static String TOKEN_VALUE ;public static String DEFAULT_TOKEN_NAME = "springMVC.token";代码片段二:自己实现一个自定义标签这里我自定义的标签叫:<dy:token/> (自定义标签的代码实现,我放csdn上了,不会的赶紧去下载,这里我不讲了),页面中使用如下:1:引入标签库:<%@ taglib prefix="dy" uri="/dy-tags"%> 2:jsp页面中的表单,注意加上token标签如下:index.jsp<%@ taglib prefix="dy" uri="/dy-tags"%><html><head><title>spring mvc</title></head><body>welcome to spring mvc!<br/><form name="mvcForm" action="indexSubmit.do" method="post"><dy:token/>username: <input name="username" type="text" value="${ername}"/>password: <input name="password" type="text" value="${user.password}"/>email: <input name="email" type="text" value="${user.email}"/><input type="submit" value="提交"></form></body></html>代码片段四:我MyController类的以下2个方法要用到token,防止表单重复提交@RequestMapping(value = "index.do")public String index(HttpServletRequest request) {return "index";}@RequestMapping(value = "indexSubmit.do", method = RequestMethod.POST)public String indexSubmit(User user,HttpServletRequestrequest) {try {myService.insert(user);("info=新增成功");} catch (Exception e) {logger.error("exception:" + e);}代码片段五:以下是我拦截器的实现,注意有两个拦截器,一个生成token,一个验证token。
spring注解重复(防重复请求)
spring注解重复(防重复请求)1、配置拦截器spring-mvc.xml<mvc:interceptors><mvc:interceptor><mvc:mapping path="/**/*"/><mvc:exclude-mapping path="/css/**"/><mvc:exclude-mapping path="/images/**"/><mvc:exclude-mapping path="/js/**"/><mvc:exclude-mapping path="/upload/**"/><bean class="mon.interceptor.AvoidDuplicateSubmissionInterceptor"/></mvc:interceptor></mvc:interceptors>2、写注解接⼝Token.javapackage mon.interceptor;import ng.annotation.ElementType;import ng.annotation.Retention;import ng.annotation.RetentionPolicy;import ng.annotation.Target;@Target(ElementType.METHOD)@Retention (RetentionPolicy.RUNTIME)public @interface Token {boolean save() default false ;boolean remove() default false ;}3、写拦截器AvoidDuplicateSubmissionInterceptor.javapackage mon.interceptor;import ng.reflect.Method;import java.util.UUID;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;public class AvoidDuplicateSubmissionInterceptor extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();Token annotation = method.getAnnotation(Token.class);if (annotation != null ) {boolean needSaveSession = annotation.save();if (needSaveSession) {request.getSession( false ).setAttribute("token", UUID.randomUUID().toString());}boolean needRemoveSession = annotation.remove();if (needRemoveSession) {if (isRepeatSubmit(request)) {return false ;}request.getSession( false ).removeAttribute( "token" );}}return true ;} else {return super .preHandle(request, response, handler);}}private boolean isRepeatSubmit(HttpServletRequest request) {String serverToken = (String) request.getSession( false ).getAttribute( "token" );if (serverToken == null ) {return true ;}String clinetToken = request.getParameter( "token" );if (clinetToken == null ) {return true ;}if (!serverToken.equals(clinetToken)) {return true ;}return false ;}}4、在需要⽣成token的⽅法上写上@Token(save=true) 在重复请求的⽅法上写上@Token(remove=true)5、在页⾯上如果是form表单写上<input type="hidden" name="token" value="${token}"/>如果是异步的则直接⽤${token}传⼊参数。
解决spring的bean同名冲突
解决spring的bean同名冲突
springboot 在启动时候,常启动不起来,检查发现是不同包下⾯有同名的service和serviceImpl,按理说不同包下是可以有同名的类存在的,但是启动就是启动不了,报错说
org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'roleServiceImpl' for bean class [com.example.service.RoleServiceImpl] conflicts with existing, non-compatible bean definition of same name and class
[com.example.roleService.RoleServiceImpl]
意思是:以Bean名字‘roleServiceImpl’注解的类[com.example.service.RoleServiceImpl]与存在的不相容的同名类
[com.example.roleService.RoleServiceImpl]相冲突。
原来是在这两个实现类上⾯都只⽤了@service这个注解,根据映射规则,这两个service都映射为了roleServiceImpl,发⽣了冲突。
解决办法,⼀:将其中⼀个实现类改为不同的名字;
⼆:将其中⼀个注解变更为⼀个name为⾮roleServiceImpl的注解@service(name="aaaa")。
再次启动,OK。
SpringBoot防止重复请求,重复表单提交超级简单的注解实现
SpringBoot防⽌重复请求,重复表单提交超级简单的注解实现1.注解接⼝/*** @description 防⽌表单重复提交注解*/@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Documentedpublic @interface DuplicateSubmitToken {//保存重复提交标记默认为需要保存boolean save() default true;}2.定义异常类/*** 重复提交异常*/public class DuplicateSubmitException extends RuntimeException {public DuplicateSubmitException(String msg) {super(msg);}public DuplicateSubmitException(String msg, Throwable cause) {super(msg, cause);}}3.拦截器/*** @description 防⽌表单重复提交拦截器*/@Aspect@Component@Slf4jpublic class DuplicateSubmitAspect {public static final String DUPLICATE_TOKEN_KEY = "duplicate_token_key";@Pointcut("execution(public * cn.test.core.controller..*(..))")public void webLog() {}@Before("webLog() && @annotation(token)")public void before(final JoinPoint joinPoint, DuplicateSubmitToken token) {if (token != null) {Object[] args = joinPoint.getArgs();HttpServletRequest request = null;HttpServletResponse response = null;for (int i = 0; i < args.length; i++) {if (args[i] instanceof HttpServletRequest) {request = (HttpServletRequest) args[i];}if (args[i] instanceof HttpServletResponse) {response = (HttpServletResponse) args[i];}}boolean isSaveSession = token.save();if (isSaveSession) {String key = getDuplicateTokenKey(joinPoint);Object t = request.getSession().getAttribute(key);if (null == t) {String uuid = UUID.randomUUID().toString();request.getSession().setAttribute(key.toString(), uuid);("token-key=" + key);("token-value=" + uuid.toString());} else {throw new DuplicateSubmitException(TextConstants.REQUEST_REPEAT);}}}}/*** 获取重复提交key-->duplicate_token_key+','+请求⽅法名** @param joinPoint* @return*/public String getDuplicateTokenKey(JoinPoint joinPoint) {String methodName = joinPoint.getSignature().getName();StringBuilder key = new StringBuilder(DUPLICATE_TOKEN_KEY);key.append(",").append(methodName);return key.toString();}@AfterReturning("webLog() && @annotation(token)")public void doAfterReturning(JoinPoint joinPoint, DuplicateSubmitToken token) {// 处理完请求,返回内容("出来⽅法:");if (token != null) {Object[] args = joinPoint.getArgs();HttpServletRequest request = null;for (int i = 0; i < args.length; i++) {if (args[i] instanceof HttpServletRequest) {request = (HttpServletRequest) args[i];}}boolean isSaveSession = token.save();if (isSaveSession) {String key = getDuplicateTokenKey(joinPoint);Object t = request.getSession().getAttribute(key);if (null != t) {//⽅法执⾏完毕移除请求重复标记request.getSession(false).removeAttribute(key);("⽅法执⾏完毕移除请求重复标记!");}}}}/*** 异常** @param joinPoint* @param e*/@AfterThrowing(pointcut = "webLog()&& @annotation(token)", throwing = "e")public void doAfterThrowing(JoinPoint joinPoint, Throwable e, DuplicateSubmitToken token) { if (null != token&& e instanceof DuplicateSubmitException == false) {//处理处理重复提交本⾝之外的异常Object[] args = joinPoint.getArgs();HttpServletRequest request = null;for (int i = 0; i < args.length; i++) {if (args[i] instanceof HttpServletRequest) {request = (HttpServletRequest) args[i];}}boolean isSaveSession = token.save();//获得⽅法名称if (isSaveSession) {String key = getDuplicateTokenKey(joinPoint);Object t = request.getSession().getAttribute(key);if (null != t) {//⽅法执⾏完毕移除请求重复标记request.getSession(false).removeAttribute(key);("异常情况--移除请求重复标记!");}}}}}4.控制器使⽤⽅法:在你想要避免重复提交的控制器⽅法添加注解@DuplicateSubmitToken即可/*** @description*/@RestControllerpublic class TestController {@RequestMapping(value = "/test", method = RequestMethod.GET)public Map<String, Object> firstResp(HttpServletRequest request) {Map<String, Object> map = new HashMap<>();request.getSession().setAttribute("request Url", request.getRequestURL());map.put("request Url", request.getRequestURL());return map;}@DuplicateSubmitToken@RequestMapping(value = "/test/d", method = RequestMethod.GET) public Map<String, Object> test(HttpServletRequest request) {Random r = new Random();int i = r.nextInt(3);if (i == 2) {throw new CustomException("有异常");}try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}Map<String, Object> map = new HashMap<>();request.getSession().setAttribute("request Url", request.getRequestURL()); map.put("request Url", request.getRequestURL());return map;}}5.测试略过。
Spring系列面试题(附答案)
Spring系列⾯试题(附答案)1、不同版本的 Spring Framework 有哪些主要功能?2、什么是 Spring Framework?Spring 是⼀个开源应⽤框架,旨在降低应⽤程序开发的复杂度。
它是轻量级、松散耦合的。
它具有分层体系结构,允许⽤户选择组件,同时还为 J2EE 应⽤程序开发提供了⼀个有凝聚⼒的框架。
它可以集成其他框架,如Structs、Hibernate、EJB 等,所以⼜称为框架的框架。
3、列举 Spring Framework 的优点。
由于 Spring Frameworks 的分层架构,⽤户可以⾃由选择⾃⼰需要的组件。
Spring Framework ⽀持 POJO(Plain Old Java Object) 编程,从⽽具备持续集成和可测试性。
由于依赖注⼊和控制反转,JDBC 得以简化。
它是开源免费的。
4、Spring Framework 有哪些不同的功能?轻量级 - Spring 在代码量和透明度⽅⾯都很轻便。
IOC - 控制反转AOP - ⾯向切⾯编程可以将应⽤业务逻辑和系统服务分离,以实现⾼内聚。
?容器 - Spring 负责创建和管理对象(Bean)的⽣命周期和配置。
MVC - 对 web 应⽤提供了⾼度可配置性,其他框架的集成也⼗分⽅便。
?事务管理- 提供了⽤于事务管理的通⽤抽象层。
Spring 的事务⽀持也可⽤于容器较少的环境。
JDBC 异常 - Spring的 JDBC 抽象层提供了⼀个异常层次结构,简化了错误处理策略。
5、Spring Framework 中有多少个模块,它们分别是什么?Spring 核⼼容器–该层基本上是Spring Framework 的核⼼。
它包含以下模块:· Spring Core· Spring Bean· SpEL (Spring Expression Language)· Spring Context数据访问/集成–该层提供与数据库交互的⽀持。
SpringMVC在对应绑定不同实体,但具有相同属性名的解决方案....
SpringMVC在对应绑定不同实体,但具有相同属性名的解决⽅案....在springmvc中,可以对前台传递过来的参数进⾏与后台实体绑定(第⼆种⽅式相对较好).⽐如:前台页⾯:1<form action="${pageContext.request.contextPath}/test/test" method="POST">2⽤户名:<input type="text" name="name"><br/>3<input type="submit" value="提交">4</form>实体类:1package com.yemaozi.rms.domain;23public class Student {4private Integer id;5private String name;6public Integer getId() {7return id;8 }9public void setId(Integer id) {10this.id = id;11 }12public String getName() {13return name;14 }15public void setName(String name) { = name;17 }18 }对应的Controller:1 @Controller2 @Scope(value="prototype")3 @RequestMapping("/test")4public class TestController {5 @RequestMapping("/test")6public String test(Student stu){7 System.out.println(stu.getName());8return "success";9 }10 }这样,在Controller是可以进⾏绑定的....但是,若是,要对多个实体数据进⾏绑定,⽽且这些实体有同名的属性....前台页⾯:<form action="${pageContext.request.contextPath}/test/test" method="POST">学⽣姓名:<input type="text" name="name"><br/>⽼师姓名:<input type="text" name="name"><br/><input type="submit" value="提交"></form>实体类:1public class Teacher {2private Integer id;3private String name;4public Integer getId() {5return id;6 }7public void setId(Integer id) {8this.id = id;9 }10public String getName() {11return name;12 }13public void setName(String name) { = name;15 }16 }Controller:1 @RequestMapping("/test")2public String test(Student stu, Teacher teacher){3 System.out.println(stu.getName() + teacher.getName());4return "success";5 }这样,就会明⽩,name并不是唯⼀标识了,所以,在后台不能精确的绑定,其实,若是将该表单进⾏提交,则会将这两个name属性分别都添加到stu 和teacher这两个对象中..因为springmvc中,是根据属性来进⾏数据绑定的,不像struts2是基于ognl的数据绑定机制.要解决现在这样问题的⽅案⼀:复合实体:即:1public class StuTeacher {2private Student stu;3private Teacher teacher;4public Student getStu() {5return stu;6 }7public void setStu(Student stu) {8this.stu = stu;9 }10public Teacher getTeacher() {11return teacher;12 }13public void setTeacher(Teacher teacher) {14this.teacher = teacher;15 }16 }创建⼀个拥有stu和teacher这两个实体对象的类StuTeacher.....这样我们就可以再前台这样书写.1<form action="${pageContext.request.contextPath}/test/test1" method="POST">2学⽣姓名:<input type="text" name=""><br/>3⽼师姓名:<input type="text" name=""><br/>4<input type="submit" value="提交">5</form>就可以根据复合实体中的属性通过.进⾏导航绑定数据在Controller中的代码:1 @RequestMapping("/test1")2public String test1(StuTeacher stuTeacher){3 System.out.println(stuTeacher);4return "success";5 }这种⽅法可以简单的处理这种数据绑定问题,好处是不需要添加任何插件代码,缺点是扩展性不好,后期可能使得代码臃肿.所以可以在springmvc中可以进⾏⾃定义ModelAttributeProcessor来进⾏数据绑定的扩展.1,⾃定义注解:1import ng.annotation.Documented;2import ng.annotation.ElementType;3import ng.annotation.Retention;4import ng.annotation.RetentionPolicy;5import ng.annotation.Target;678 @Target({ElementType.PARAMETER, ElementType.METHOD})9 @Retention(RetentionPolicy.RUNTIME)10 @Documented11public @interface ExtModelAttribute {12 String value() default "";13 }2,继承ServletModelAttributeMethodProcessor类,实现⾃⼰的数据绑定模式.1import javax.servlet.ServletRequest;23import org.springframework.core.MethodParameter;4import org.springframework.web.bind.ServletRequestDataBinder;5import org.springframework.web.bind.WebDataBinder;6import org.springframework.web.context.request.NativeWebRequest;7import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor; 89public class ExtServletModelAttributeMethodProcessor extends10 ServletModelAttributeMethodProcessor {1112public ExtServletModelAttributeMethodProcessor() {13super(false);14 }1516public ExtServletModelAttributeMethodProcessor(boolean annotationNotRequired) {17super(annotationNotRequired);18 }1920 @Override21public boolean supportsParameter(MethodParameter parameter) {22if (parameter.hasParameterAnnotation(ExtModelAttribute.class)) {23return true;24 } else {25return false;26 }27 }2829 @Override30protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {31 ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);32 ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;33 servletBinder.setFieldDefaultPrefix(servletBinder.getObjectName() + ".");34 servletBinder.bind(servletRequest);35 }36 }3,在springmvc配置⽂件中添加相应的加载驱动配置1<mvc:annotation-driven>2<!--添加在此处-->3<mvc:argument-resolvers>4<bean class="com.yemaozi.springmvc.ext.databind.ExtServletModelAttributeMethodProcessor"/>5</mvc:argument-resolvers>6</mvc:annotation-driven>4,应⽤在前台页⾯中:<form action="${pageContext.request.contextPath}/test/test1" method="POST">学⽣姓名:<input type="text" name=""><br/>⽼师姓名:<input type="text" name=""><br/><input type="submit" value="提交"></form>在Controller中使⽤⽅式:1 @RequestMapping("/test2")2public String test2(@ExtModelAttribute("stu") Student stu, @ExtModelAttribute("teacher")Teacher teacher){ 3 System.out.println(stu.getName() + teacher.getName());4return "success";5 }使⽤刚才⾃定义的注解来标注对应的属性.。
Spring5.0源码分析相关知识点
Spring5.0源码分析相关知识点1、spring中注⼊的beanId如果重复会怎么样?答:启动报错,因为beanId和类名是⼀样的;如果bean的全路径⼀致,且beanId相同会报错;对于相同类如果想要注⼊多个bean可修改beanId即可;2、注解@Configuration作⽤答:是将实例类作为配置⽂件注⼊spring容器中,等同于spring的xml配置⽂件;在该类中通过@Bean注解将bean对像注⼊到容器中;与xml 配置中注⼊bean是通过<bean>标签的⽅法是⼀样的;[⼀般这种⽤法是对外部jar包注⼊的时候,同部的有其他] 注解模式通过applicationContext = new AnnotationConfigApplicationContext(MySpringConfig.class)spring拿到该配置的上下⽂,再通过上下⽂ applicationContext.getBean("userEntity", UserEntity.class);拿到实例类; xml模式通过applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");拿到配置上下⽂对像,再通过上下⽂对像获取bean,applicationContext .getBean("userEntity");3、将实例类注⼊到spring容器中的作⽤答:实例类注⼊到spring容器中,通过spring的IOC来管理对像的创建与使⽤;4、注解@ComponentScan答:spring中其他可以注⼊IOC的注解有,@Service,@Controller,@Repository,底层都是@Component,加上这些注解通再通过@ComponentScan扫包,在启动的时候就会将该类注⼊到springIOC中;includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)}要扫的注解;其他不注⼊excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)}排除要扫的注解;其他注⼊5、怎么查看springIOC中有那些bean对像?答:String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();在spring容器中通过上下⽂的getBeanDefinitionNames⽅法拿到IOC容器中的bean对像名称,注意是名称;6、spring容器中的对像默认情况下是单例还是多例?注解@Scope作⽤答:spring中的数据默认情况下是单例;可以通过注解@Scope修改为多例;参数如下 singleton单例模式【默认】 prototype原型模式:每次获取Bean的时候都会有⼀个新的实例。
SpringBoot使用AOP防止重复提交的方法示例
SpringBoot使⽤AOP防⽌重复提交的⽅法⽰例在传统的web项⽬中,防⽌重复提交,通常做法是:后端⽣成⼀个唯⼀的提交令牌(uuid),并存储在服务端。
页⾯提交请求携带这个提交令牌,后端验证并在第⼀次验证后删除该令牌,保证提交请求的唯⼀性。
上述的思路其实没有问题的,但是需要前后端都稍加改动,如果在业务开发完在加这个的话,改动量未免有些⼤了,本节的实现⽅案⽆需前端配合,纯后端处理。
思路1. ⾃定义注解 @NoRepeatSubmit 标记所有Controller中的提交请求2. 通过AOP 对所有标记了 @NoRepeatSubmit 的⽅法拦截3. 在业务⽅法执⾏前,获取当前⽤户的 token(或者JSessionId)+ 当前请求地址,作为⼀个唯⼀ KEY,去获取 Redis 分布式锁(如果此时并发获取,只有⼀个线程会成功获取锁)4. 业务⽅法执⾏后,释放锁关于Redis 分布式锁不了解的同学戳这⾥ ==>使⽤Redis 是为了在负载均衡部署,如果是单机的部署的项⽬可以使⽤⼀个线程安全的本地Cache 替代 RedisCode这⾥只贴出 AOP 类和测试类,完整代码见 ==>@Aspect@Componentpublic class RepeatSubmitAspect {private static final Logger LOGGER = LoggerFactory.getLogger(RepeatSubmitAspect.class);@Autowiredprivate RedisLock redisLock;@Pointcut("@annotation(com.gitee.taven.aop.NoRepeatSubmit)")public void pointCut() {}@Around("pointCut()")public Object before(ProceedingJoinPoint pjp) {try {HttpServletRequest request = RequestUtils.getRequest();Assert.notNull(request, "request can not null");// 此处可以⽤token或者JSessionIdString token = request.getHeader("Authorization");String path = request.getServletPath();String key = getKey(token, path);String clientId = getClientId();boolean isSuccess = redisLock.tryLock(key, clientId, 10);("tryLock key = [{}], clientId = [{}]", key, clientId);if (isSuccess) {("tryLock success, key = [{}], clientId = [{}]", key, clientId);// 获取锁成功, 执⾏进程Object result = pjp.proceed();// 解锁redisLock.releaseLock(key, clientId);("releaseLock success, key = [{}], clientId = [{}]", key, clientId);return result;} else {// 获取锁失败,认为是重复提交的请求("tryLock fail, key = [{}]", key);return new ApiResult(200, "重复请求,请稍后再试", null);}} catch (Throwable throwable) {throwable.printStackTrace();}return new ApiResult(500, "系统异常", null);}private String getKey(String token, String path) {return token + path;}private String getClientId() {return UUID.randomUUID().toString();}}多线程测试测试代码如下,模拟⼗个请求并发同时提交@Componentpublic class RunTest implements ApplicationRunner {private static final Logger LOGGER = LoggerFactory.getLogger(RunTest.class);@Autowiredprivate RestTemplate restTemplate;@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("执⾏多线程测试");String url="http://localhost:8000/submit";CountDownLatch countDownLatch = new CountDownLatch(1);ExecutorService executorService = Executors.newFixedThreadPool(10);for(int i=0; i<10; i++){String userId = "userId" + i;HttpEntity request = buildRequest(userId);executorService.submit(() -> {try {countDownLatch.await();System.out.println("Thread:"+Thread.currentThread().getName()+", time:"+System.currentTimeMillis());ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);System.out.println("Thread:"+Thread.currentThread().getName() + "," + response.getBody());} catch (InterruptedException e) {e.printStackTrace();}});}countDownLatch.countDown();}private HttpEntity buildRequest(String userId) {HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);headers.set("Authorization", "yourToken");Map<String, Object> body = new HashMap<>();body.put("userId", userId);return new HttpEntity<>(body, headers);}}成功防⽌重复提交,控制台⽇志如下,可以看到⼗个线程的启动时间⼏乎同时发起,只有⼀个请求提交成功了本节demo戳这⾥ ==>build项⽬之后,启动本地redis,运⾏项⽬⾃动执⾏测试⽅法以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
继承类 避免同名方法
继承类避免同名方法什么是继承类?继承是面向对象编程中的一个重要概念,它允许一个类继承另一个类的属性和方法。
被继承的类称为父类或基类,继承这个父类的类称为子类或派生类。
子类可以继承父类的属性和方法,并且可以添加自己的属性和方法。
继承类的概念使得代码的重用更加容易,可以减少代码的冗余,提高代码的可维护性和可扩展性。
在继承关系中,子类可以重用父类的代码,并且可以根据需要进行修改和扩展。
避免同名方法的问题在继承关系中,子类继承了父类的方法,但是有时候子类需要对继承的方法进行修改或者扩展。
这时候就有可能出现同名方法的问题。
如果子类定义了与父类同名的方法,那么子类的方法会覆盖父类的方法,这就导致了父类的方法无法被子类继承和使用。
这种情况下,子类只能访问自己定义的方法,而无法访问父类的方法。
同名方法的问题可能导致代码的混乱和错误的结果。
因此,在继承类的设计中,需要避免同名方法的出现,以确保代码的正确性和可维护性。
如何避免同名方法?为了避免同名方法的问题,在继承类的设计中需要遵循以下几个原则:1. 使用不同的方法名为了避免同名方法的问题,子类应该尽量避免使用与父类相同的方法名。
可以在子类中使用不同的方法名,以区分子类的方法和父类的方法。
2. 使用不同的参数列表如果子类需要对父类的方法进行修改或者扩展,可以在子类的方法中添加额外的参数,以实现不同的功能。
这样可以避免同名方法的问题,同时也可以提供更多的灵活性和功能性。
3. 使用super关键字调用父类方法如果子类需要在重写的方法中调用父类的方法,可以使用super关键字来调用父类的方法。
super关键字可以在子类中访问父类的属性和方法,以实现对父类方法的扩展和修改。
示例代码下面是一个示例代码,演示了如何在继承类中避免同名方法的问题:class ParentClass:def __init__(self, name): = namedef say_hello(self):print("Hello, {}".format())class ChildClass(ParentClass):def __init__(self, name, age):super().__init__(name)self.age = agedef say_hello_with_age(self):super().say_hello()print("I am {} years old.".format(self.age))在上面的代码中,ParentClass是一个父类,定义了一个say_hello方法,用于打印问候语。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
spring巧用继承解决bean的id相同的问题需求是这样的:我们的项目中引用了两个jar包,这两个jar包是其他项目组提供的,不能修改!奇葩的是:这两个jar中都需要引用方提供一个相同id的bean,而bean的定义却是不同的,也就是虽然id相同,但他们对应的却是两个不同的Java类,导致出现的问题是:该id对应的java类满足了第一个jar 包的要求,则不能满足第二个jar包的要求,满足了第二个jar包的要求,则不能满足第一个jar包的要求,导致spring容器在启动时就报错。
那么,该怎么解决该问题呢?如何做才能符合两个jar包的需求呢?经过仔细思考:发现通过java类的继承可以巧妙的实现该需求,现示例如下:spring主配置文件:applicationContext.xml[html] view plain copy print?在CODE上查看代码片派生到我的代码片<?xml version="1.0" encoding="UTF-8"?><beansxmlns="/schema/beans"xmlns:xsi="/2001/XMLSchema-instance"xmlns:p="/schema/p"xsi:schemaLocation="/schema/beans/schema/beans/spring-beans-3.1.xsd"><import resource="classpath*:app1.xml"/><import resource="classpath*:app2.xml"/><import resource="classpath:appContext1.xml"/><!-- <import resource="classpath:appContext2.xml"/> --></beans>其中:app1.xml和app2.xml是两个jar包app1.jar和app2.jar中对应的spring配置文件app1.jar和app2.jar中主要就有一个java编译好后的class文件和一个配置文件:其中app1.jar中的java文件和配置文件分别为:Test1.java[java] view plain copy print?在CODE上查看代码片派生到我的代码片package mypackage;public class Test1 {private Object obj;public Object getObj() {return obj;}public void setObj(Object obj) {this.obj = obj;}@Overridepublic String toString() {if(!this.getObj().getClass().getName().equals("mypackage.Obj1")){try {throw new Exception();} catch (Exception e) {System.err.println("test1--->mypackage.Test1依赖的obj类型错误,注入的不是mypacke.Obj1");}}System.out.println("package.Test1");return "package.Test1";}}app1.xml[html] view plain copy print?在CODE上查看代码片派生到我的代码片<?xml version="1.0" encoding="UTF-8"?><beansxmlns="/schema/beans"xmlns:xsi="/2001/XMLSchema-instance"xmlns:p="/schema/p"xsi:schemaLocation="/schema/beans/schema/beans/spring-beans-3.1.xsd"><bean id="test1" class="mypackage.Test1"><property name="obj"><ref bean="mybean"/></property></bean></beans>app2.jar中的java类和配置文件分别为:Test2.java[java] view plain copy print?在CODE上查看代码片派生到我的代码片package mypackage;public class Test2 {private Object obj;public Object getObj() {return obj;}public void setObj(Object obj) {this.obj = obj;}@Overridepublic String toString() {if(!this.getObj().getClass().getName().equals("mypackage.Obj2")){try {throw new Exception();} catch (Exception e) {System.err.println("test2--->mypackage.Test2依赖的obj类型错误,注入的不是mypacke.Obj2");}}System.out.println("package.Test2");return "package.Test2";}}app2.xml:[html] view plain copy print?在CODE上查看代码片派生到我的代码片<?xml version="1.0" encoding="UTF-8"?><beansxmlns="/schema/beans"xmlns:xsi="/2001/XMLSchema-instance"xmlns:p="/schema/p"xsi:schemaLocation="/schema/beans/schema/beans/spring-beans-3.1.xsd"><bean id="test2" class="mypackage.Test2"><property name="obj"><ref bean="mybean"/></property></bean></beans>其中:mybean是需要引用方提供的,这两个jar中需要引用方提供的bean的id都是一样的,这就导致了文章开始所提到的的问题。
测试程序:[java] view plain copy print?在CODE上查看代码片派生到我的代码片package mypackage;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");/* System.out.println(((Test1)context.getBean("child1")).getObj().getClass().getName());System.out.println(((Test1)context.getBean("child2")).getObj().getClass().getName());*/System.out.println(((Test1)context.getBean("test1")).getObj().getClass().getName());System.out.println(((Test2)context.getBean("test2")).getObj().getClass().getName());}}运行得到的两个bean是相同的bean,不符合要求!!解决方案:引进两个子类Child1和Child2,分别继承jar包需要的Test1和Test2Child1.java[java] view plain copy print?在CODE上查看代码片派生到我的代码片package mypackage;public class Child1 extends T est1 {private Object obj;public Object getObj() {return obj;}public void setObj(Object obj) {this.obj = obj;}@Overridepublic String toString() {if(!this.getObj().getClass().getName().equals("mypackage.Hello")){try {throw new Exception();} catch (Exception e) {System.err.println("child1--->mypackage.Test依赖的obj注入的类型错误,注入的不是mypacke.Hello");}}return "package.Test1";}}Child2.java[java] view plain copy print?在CODE上查看代码片派生到我的代码片package mypackage;public class Child2 extends T est2 {private Object obj;public Object getObj() {return obj;}public void setObj(Object obj) {this.obj = obj;}public String toString() {if(!this.getObj().getClass().getName().equals("mypackage.World")){try {throw new Exception();} catch (Exception e) {System.err.println("child2--->mypackage.Test2依赖的obj注入的类型错误,注入的不是mypacke.World");}}return "package.Test2";}}添加一个新的spring辅助配置文件:[java] view plain copy print?在CODE上查看代码片派生到我的代码片<?xml version="1.0" encoding="UTF-8"?><beansxmlns="/schema/beans"xmlns:xsi="/2001/XMLSchema-instance"xmlns:p="/schema/p"xsi:schemaLocation="/schema/beans/schema/beans/spring-beans-3.1.xsd"><bean id="obj1" class="mypackage.Obj1"></bean><bean id="obj2" class="mypackage.Obj2"></bean><bean id="mybean1" class="mypackage.Obj2"></bean><bean id="mybean2" class="mypackage.Obj2"></bean><bean id="child1" class="mypackage.Child1"><property name="obj"><ref bean="mybean1"/></property></bean><bean id="child2" class="mypackage.Child2"><property name="obj"><ref bean="mybean2"/></property></beans>在spring主配置文件做修改如下:[html] view plain copy print?在CODE上查看代码片派生到我的代码片<?xml version="1.0" encoding="UTF-8"?><beansxmlns="/schema/beans"xmlns:xsi="/2001/XMLSchema-instance"xmlns:p="/schema/p"xsi:schemaLocation="/schema/beans/schema/beans/spring-beans-3.1.xsd"><!-- <import resource="classpath*:app1.xml"/><import resource="classpath*:app2.xml"/> --><import resource="classpath:appContext1.xml"/><import resource="classpath:appContext2.xml"/></beans>测试程序:[java] view plain copy print?在CODE上查看代码片派生到我的代码片package mypackage;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//(context);/*System.out.println(((Test2)(context.getBean("test2"))).getObj().getClass().getName());System.out.println(((Test)(context.getBean("test"))).getObj().getClass().getName());*///System.out.println(context.getBean("mybean").getClass().getName());System.out.println(context.getBean("child1"));System.out.println(context.getBean("child2"));/* System.out.println(context.getBean("mypackage.Hello"));System.out.println(context.getBean("mypackage.Hello#1"));*/}}运行得到:获取的是不同的bean,也就是间接实现了两个jar包中需要提供同名id的bean,但bean对应的java类是不同的java类的需求!总结:实际上就是面向对象的lisp原则,就是里氏替换原则,具体点就是凡是父类出现的地方,都可以用子类来代替!例子很简单,但是能说明问题。