Announcement Announcement Module
Collapse
No announcement yet.
CGLIB Autowiring Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • CGLIB Autowiring

    Hi guys,

    I'm attempting to make a method on my one of my controllers transactional, but my auto-wiring is not working. The controller doesn't implement an interface, so to my understanding, Spring will attempt to proxy it using CGLIB, which means it needs an empty constructor. However, whether I use auto-wired setters or provide an additional auto-wired constructor, the fields always remain null when the transactional method is called. Is there a proper way to auto-wire a CGLIB proxied bean?

    My controller looks like this:

    Code:
    @Controller
    @RequestMapping("/promote")
    public class PromotionController {
    
        protected Gson gson;
        protected ProjectDao projectDao;
        protected ProspectDao prospectDao;
    
        @Transactional
        @RequestMapping(method = {RequestMethod.POST, RequestMethod.PUT})
        public final HttpEntity<String> promote(@RequestBody final String promotionJson) {
    
        	Promotion promotion = gson.fromJson(promotionJson, Promotion.class);
    
        	Prospect target = promotion.getTarget();
        	Project result = promotion.getResult();
    
            projectDao.addEntity(result);
    
            result.setCommentStack(target.getCommentStack());
            projectDao.mergeEntity(result);
    
            prospectDao.removeEntity(target.getId());
    
            return JsonEntity.create(gson.toJson(result));
        }
    
        @Autowired
        public final void setProjectDao(ProjectDao projectDao) {
        	this.projectDao = projectDao;
        }
    
        @Autowired
        public final void setProspectDao(ProspectDao prospectDao) {
        	this.prospectDao = prospectDao;
        }
    
        @Autowired
        public final void setGsonBuilder(GsonBuilder gsonBuilder) {
        	 this.gson = gsonBuilder.create();
        }
    }
    Like I said, I've tried providing a separate constructor too, but neither of these approaches are working; debugging shows that prospectDao, projectDao and gson remain null, which lead me to believe the auto-wiring is simply not happening. But I have no idea why this is. Can anyone offer some help?

  • #2
    You also need to provide your context configuration. Are you enabling <context:annotation-config> and using <context:component-scan> in your context config. These are reqd for annotation configuration to work correctly. Also can you elaborate why are you declaring your setters as final. The created proxy won't be able to override any of these methods as they're final.

    Comment


    • #3
      For starters I think it is a bad idea to make your controller transactional, the controller should be a thin integration layer which calls the service layer, which should be transactional not your controller.

      You don't need setters simply put @Autowired on the field and spring will do the injection for you. To have injection working you need either context:component-scan in your configuration (if you scan for @Controllers) if you manually define the controllers you need a context:annotation-config to enable injection.

      Controller
      Code:
      @Controller
      @RequestMapping("/promote")
      public class PromotionController {
      
          @Autowired
      	protected Gson gson;
      	
      	@Autowired
      	private PromotionService promotionService;
      
          @RequestMapping(method = {RequestMethod.POST, RequestMethod.PUT})
          public final HttpEntity<String> promote(@RequestBody final String promotionJson) {
          	Promotion promotion = gson.fromJson(promotionJson, Promotion.class);
      		Project result = promotionService.someMeaningfulBusinessName(promotion);
              return JsonEntity.create(gson.toJson(result));
          }
      
      }
      Service
      Code:
      @Service
      @Transactional
      public class DefaultPromotionService implements PromotionService {
      	
      	@Autowired
          protected ProjectDao projectDao;
      	@Autowired
          protected ProspectDao prospectDao;
      
      	
              @Override
      	public Project someMeaningfulBusinessName(Promotion promotion) {
          	  Prospect target = promotion.getTarget();
          	  Project result = promotion.getResult();
      
                result.setCommentStack(target.getCommentStack());
      
                 projectDao.addEntity(result);
                 prospectDao.removeEntity(target.getId());
      
      	}
      }
      This way you also don't need cglib as you are simply using interfaces (assuming your daos are also using interfaces).

      Comment


      • #4
        Originally posted by objectamit View Post
        You also need to provide your context configuration. Are you enabling <context:annotation-config> and using <context:component-scan> in your context config. These are reqd for annotation configuration to work correctly. Also can you elaborate why are you declaring your setters as final. The created proxy won't be able to override any of these methods as they're final.
        We do have the have these in our config; we have a number of working controllers but this was the first we felt the needed to make one transactional. Good point about the final setters though, that was an oversight by myself. I assume it's a problem because the CGLIB proxy creates subclasses, which I didn't take into proper consideration. I'll remove those.

        Originally posted by Marten Deinum View Post
        For starters I think it is a bad idea to make your controller transactional, the controller should be a thin integration layer which calls the service layer, which should be transactional not your controller.

        You don't need setters simply put @Autowired on the field and spring will do the injection for you. To have injection working you need either context:component-scan in your configuration (if you scan for @Controllers) if you manually define the controllers you need a context:annotation-config to enable injection.

        Controller
        Code:
        @Controller
        @RequestMapping("/promote")
        public class PromotionController {
        
            @Autowired
        	protected Gson gson;
        	
        	@Autowired
        	private PromotionService promotionService;
        
            @RequestMapping(method = {RequestMethod.POST, RequestMethod.PUT})
            public final HttpEntity<String> promote(@RequestBody final String promotionJson) {
            	Promotion promotion = gson.fromJson(promotionJson, Promotion.class);
        		Project result = promotionService.someMeaningfulBusinessName(promotion);
                return JsonEntity.create(gson.toJson(result));
            }
        
        }
        Service
        Code:
        @Service
        @Transactional
        public class DefaultPromotionService implements PromotionService {
        	
        	@Autowired
            protected ProjectDao projectDao;
        	@Autowired
            protected ProspectDao prospectDao;
        
        	
                @Override
        	public Project someMeaningfulBusinessName(Promotion promotion) {
            	  Prospect target = promotion.getTarget();
            	  Project result = promotion.getResult();
        
                  result.setCommentStack(target.getCommentStack());
        
                   projectDao.addEntity(result);
                   prospectDao.removeEntity(target.getId());
        
        	}
        }
        This way you also don't need cglib as you are simply using interfaces (assuming your daos are also using interfaces).
        Thanks for this Marten, I think I'll take this approach. Having the controller as transactional was actually a point of discussion in the team, the argument goes that what we wanted to do for promotion was business logic and therefore should not be in a DAO, but noone knew to use services.

        After some light reading it seems we should let our controller simply be a URL mapping that passes data to a service, which does validation and business logic before saving that data through a DAO. Is this pattern correct, and something we should use for every controller?

        Comment


        • #5
          After some light reading it seems we should let our controller simply be a URL mapping that passes data to a service, which does validation and business logic before saving that data through a DAO. Is this pattern correct, and something we should use for every controller?
          Basically yes... Your Service layer should be the layer containing or kicking of the business logic, it probably needs some daos/repositories to get and save the data (although when using JPA this can be argued about imho). The web layer should simply be a thin layer which validates the incoming request, creates the appropriate object and then calls a service method.

          No you also have a single location for your business logic which allows you to also simply test your business logic and also allows it for it to be reused (maybe you need to execute it based on an incoming JMS message or normal web service call also for instance).

          Comment


          • #6
            Originally posted by Marten Deinum View Post
            Basically yes... Your Service layer should be the layer containing or kicking of the business logic, it probably needs some daos/repositories to get and save the data (although when using JPA this can be argued about imho). The web layer should simply be a thin layer which validates the incoming request, creates the appropriate object and then calls a service method.

            No you also have a single location for your business logic which allows you to also simply test your business logic and also allows it for it to be reused (maybe you need to execute it based on an incoming JMS message or normal web service call also for instance).
            Yeah, I can see why it would be beneficial to us. I'm going to take a stab at this now. Thanks for you help.

            Comment

            Working...
            X